1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::error::{AuthErrorOr, Error};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents an access token returned by oauth2 servers. All access tokens are
/// Bearer tokens. Other types of tokens are not supported.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct AccessToken {
  value: String,
  expires_at: Option<DateTime<Utc>>,
}

impl AccessToken {
  /// A string representation of the access token.
  pub fn as_str(&self) -> &str {
    &self.value
  }

  /// The time the access token will expire, if any.
  pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
    self.expires_at
  }

  /// Determine if the access token is expired.
  /// This will report that the token is expired 1 minute prior to the
  /// expiration time to ensure that when the token is actually sent to the
  /// server it's still valid.
  pub fn is_expired(&self) -> bool {
    // Consider the token expired if it's within 1 minute of it's expiration
    // time.
    self
      .expires_at
      .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
      .unwrap_or(false)
  }
}

impl AsRef<str> for AccessToken {
  fn as_ref(&self) -> &str {
    self.as_str()
  }
}

impl From<TokenInfo> for AccessToken {
  fn from(value: TokenInfo) -> Self {
    AccessToken {
      value: value.access_token,
      expires_at: value.expires_at,
    }
  }
}

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expiry date.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub(crate) struct TokenInfo {
  /// used when authenticating calls to oauth2 enabled services.
  pub(crate) access_token: String,
  /// used to refresh an expired access_token.
  pub(crate) refresh_token: Option<String>,
  /// The time when the token expires.
  pub(crate) expires_at: Option<DateTime<Utc>>,
}

impl TokenInfo {
  pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
    #[derive(Deserialize)]
    struct RawToken {
      access_token: String,
      refresh_token: Option<String>,
      token_type: String,
      expires_in: Option<i64>,
    }

    let RawToken {
      access_token,
      refresh_token,
      token_type,
      expires_in,
    } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;

    if token_type.to_lowercase().as_str() != "bearer" {
      use std::io;
      return Err(
        io::Error::new(
          io::ErrorKind::InvalidData,
          format!(
            r#"unknown token type returned; expected "bearer" found {}"#,
            token_type
          ),
        )
        .into(),
      );
    }

    let expires_at =
      expires_in.map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));

    Ok(TokenInfo {
      access_token,
      refresh_token,
      expires_at,
    })
  }
  /// Returns true if we are expired.
  pub fn is_expired(&self) -> bool {
    self
      .expires_at
      .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
      .unwrap_or(false)
  }
}