use crate::idtoken::IdToken;
use crate::jwt::{Claim, JsonWebToken, JwtParser};
use chrono::{DateTime, Duration, TimeZone, Utc};
use chrono_humanize::HumanTime;
use from_as::*;
use graph_error::{GraphFailure, WithGraphError, WithGraphErrorAsync};
use serde_aux::prelude::*;
use std::convert::TryFrom;
use std::fmt;
use std::io::{Read, Write};
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, AsFile, FromFile)]
pub struct AccessToken {
access_token: String,
token_type: String,
#[serde(deserialize_with = "deserialize_number_from_string")]
expires_in: i64,
scope: Option<String>,
refresh_token: Option<String>,
user_id: Option<String>,
id_token: Option<String>,
state: Option<String>,
timestamp: Option<DateTime<Utc>>,
#[serde(skip)]
jwt: Option<JsonWebToken>,
}
impl AccessToken {
pub fn new(token_type: &str, expires_in: i64, scope: &str, access_token: &str) -> AccessToken {
let mut token = AccessToken {
token_type: token_type.into(),
expires_in,
scope: Some(scope.into()),
access_token: access_token.into(),
refresh_token: None,
user_id: None,
id_token: None,
state: None,
timestamp: Some(Utc::now() + Duration::seconds(expires_in)),
jwt: None,
};
token.parse_jwt();
token
}
pub fn set_token_type(&mut self, s: &str) -> &mut AccessToken {
self.token_type = s.into();
self
}
pub fn set_expires_in(&mut self, expires_in: i64) -> &mut AccessToken {
self.expires_in = expires_in;
self.timestamp = Some(Utc::now() + Duration::seconds(expires_in));
self
}
pub fn set_scope(&mut self, s: &str) -> &mut AccessToken {
self.scope = Some(s.to_string());
self
}
pub fn set_bearer_token(&mut self, s: &str) -> &mut AccessToken {
self.access_token = s.into();
self
}
pub fn set_refresh_token(&mut self, s: &str) -> &mut AccessToken {
self.refresh_token = Some(s.to_string());
self
}
pub fn set_user_id(&mut self, s: &str) -> &mut AccessToken {
self.user_id = Some(s.to_string());
self
}
pub fn set_id_token(&mut self, s: &str) -> &mut AccessToken {
self.id_token = Some(s.to_string());
self
}
pub fn with_id_token(&mut self, id_token: IdToken) {
self.id_token = Some(id_token.get_id_token());
}
pub fn set_state(&mut self, s: &str) -> &mut AccessToken {
self.state = Some(s.to_string());
self
}
pub fn gen_timestamp(&mut self) {
self.timestamp = Some(Utc::now() + Duration::seconds(self.expires_in));
}
pub fn token_type(&self) -> &str {
self.token_type.as_str()
}
pub fn expires_in(&self) -> i64 {
self.expires_in
}
pub fn scopes(&self) -> Option<&String> {
self.scope.as_ref()
}
pub fn bearer_token(&self) -> &str {
self.access_token.as_str()
}
pub fn user_id(&self) -> Option<String> {
self.user_id.clone()
}
pub fn refresh_token(&self) -> Option<String> {
self.refresh_token.clone()
}
pub fn id_token(&self) -> Option<String> {
self.id_token.clone()
}
pub fn state(&self) -> Option<String> {
self.state.clone()
}
pub fn timestamp(&self) -> Option<DateTime<Utc>> {
self.timestamp
}
pub fn is_expired(&self) -> bool {
if let Some(human_time) = self.elapsed() {
return human_time.le(&HumanTime::from(Duration::seconds(0)));
}
true
}
pub fn elapsed(&self) -> Option<HumanTime> {
if let Some(timestamp) = self.timestamp {
let ht = HumanTime::from(timestamp);
return Some(ht);
}
None
}
fn parse_jwt(&mut self) {
let mut set_timestamp = false;
if let Ok(jwt) = JwtParser::parse(self.bearer_token()) {
if let Some(claims) = jwt.claims() {
if let Some(claim) = claims
.iter()
.find(|item| item.key().eq(&String::from("exp")))
{
let value = claim.value();
let number = value.as_i64().unwrap();
self.timestamp = Some(Utc.timestamp(number, 0));
set_timestamp = true;
}
}
self.jwt = Some(jwt);
}
if !set_timestamp {
self.gen_timestamp();
}
}
pub fn claims(&self) -> Option<Vec<Claim>> {
self.jwt.as_ref()?.claims()
}
pub fn jwt(&self) -> Option<&JsonWebToken> {
self.jwt.as_ref()
}
pub(crate) async fn try_from_async(
builder: reqwest::RequestBuilder,
) -> Result<AccessToken, GraphFailure> {
let mut access_token = builder
.send()
.await?
.with_graph_error()
.await?
.json::<AccessToken>()
.await?;
access_token.parse_jwt();
Ok(access_token)
}
}
impl Default for AccessToken {
fn default() -> Self {
AccessToken {
token_type: String::new(),
expires_in: 0,
scope: None,
access_token: String::new(),
refresh_token: None,
user_id: None,
id_token: None,
state: None,
timestamp: Some(Utc::now() + Duration::seconds(0)),
jwt: None,
}
}
}
impl TryFrom<&str> for AccessToken {
type Error = GraphFailure;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut access_token: AccessToken = serde_json::from_str(value)?;
access_token.parse_jwt();
Ok(access_token)
}
}
impl TryFrom<reqwest::blocking::RequestBuilder> for AccessToken {
type Error = GraphFailure;
fn try_from(value: reqwest::blocking::RequestBuilder) -> Result<Self, Self::Error> {
let response = value.send()?;
let access_token: AccessToken = AccessToken::try_from(response)?;
Ok(access_token)
}
}
impl TryFrom<Result<reqwest::blocking::Response, reqwest::Error>> for AccessToken {
type Error = GraphFailure;
fn try_from(
value: Result<reqwest::blocking::Response, reqwest::Error>,
) -> Result<Self, Self::Error> {
let response = value?;
AccessToken::try_from(response)
}
}
impl TryFrom<reqwest::blocking::Response> for AccessToken {
type Error = GraphFailure;
fn try_from(value: reqwest::blocking::Response) -> Result<Self, Self::Error>
where
Self: for<'de> serde::Deserialize<'de>,
{
let mut access_token = value.with_graph_error()?.json::<AccessToken>()?;
access_token.parse_jwt();
Ok(access_token)
}
}
impl fmt::Debug for AccessToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AccessToken")
.field("bearer_token", &"[REDACTED]")
.field("token_type", &self.token_type)
.field("expires_in", &self.expires_in)
.field("scope", &self.scope)
.field("user_id", &self.user_id)
.field("id_token", &"[REDACTED]")
.field("state", &self.state)
.field("timestamp", &self.timestamp)
.finish()
}
}