service_authenticator/
types.rs

1use crate::error::{AuthErrorOr, Error};
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// Represents an access token returned by oauth2 servers. All access tokens are
7/// Bearer tokens. Other types of tokens are not supported.
8#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
9pub struct AccessToken {
10  value: String,
11  expires_at: Option<DateTime<Utc>>,
12}
13
14impl AccessToken {
15  /// A string representation of the access token.
16  pub fn as_str(&self) -> &str {
17    &self.value
18  }
19
20  /// The time the access token will expire, if any.
21  pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
22    self.expires_at
23  }
24
25  /// Determine if the access token is expired.
26  /// This will report that the token is expired 1 minute prior to the
27  /// expiration time to ensure that when the token is actually sent to the
28  /// server it's still valid.
29  pub fn is_expired(&self) -> bool {
30    // Consider the token expired if it's within 1 minute of it's expiration
31    // time.
32    self
33      .expires_at
34      .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
35      .unwrap_or(false)
36  }
37}
38
39impl AsRef<str> for AccessToken {
40  fn as_ref(&self) -> &str {
41    self.as_str()
42  }
43}
44
45impl From<TokenInfo> for AccessToken {
46  fn from(value: TokenInfo) -> Self {
47    AccessToken {
48      value: value.access_token,
49      expires_at: value.expires_at,
50    }
51  }
52}
53
54/// Represents a token as returned by OAuth2 servers.
55///
56/// It is produced by all authentication flows.
57/// It authenticates certain operations, and must be refreshed once
58/// it reached it's expiry date.
59#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
60pub(crate) struct TokenInfo {
61  /// used when authenticating calls to oauth2 enabled services.
62  pub(crate) access_token: String,
63  /// used to refresh an expired access_token.
64  pub(crate) refresh_token: Option<String>,
65  /// The time when the token expires.
66  pub(crate) expires_at: Option<DateTime<Utc>>,
67}
68
69impl TokenInfo {
70  pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
71    #[derive(Deserialize)]
72    struct RawToken {
73      access_token: String,
74      refresh_token: Option<String>,
75      token_type: String,
76      expires_in: Option<i64>,
77    }
78
79    let RawToken {
80      access_token,
81      refresh_token,
82      token_type,
83      expires_in,
84    } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;
85
86    if token_type.to_lowercase().as_str() != "bearer" {
87      use std::io;
88      return Err(
89        io::Error::new(
90          io::ErrorKind::InvalidData,
91          format!(
92            r#"unknown token type returned; expected "bearer" found {}"#,
93            token_type
94          ),
95        )
96        .into(),
97      );
98    }
99
100    let expires_at =
101      expires_in.map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));
102
103    Ok(TokenInfo {
104      access_token,
105      refresh_token,
106      expires_at,
107    })
108  }
109  /// Returns true if we are expired.
110  pub fn is_expired(&self) -> bool {
111    self
112      .expires_at
113      .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
114      .unwrap_or(false)
115  }
116}