mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2026-01-02 09:26:16 +01:00
Improve Token
Remove expires_in in favor of only having an expires_at DateTime field. Add a from_json method that deserializes from json data into the appropriate Token (or Error) and use that consistently throughout the codebase.
This commit is contained in:
@@ -12,7 +12,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10"
|
||||
chrono = "0.4"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
http = "0.1"
|
||||
hyper = {version = "0.13.0-alpha.4", features = ["unstable-stream"]}
|
||||
hyper-rustls = "=0.18.0-alpha.2"
|
||||
|
||||
@@ -208,9 +208,7 @@ impl DeviceFlow {
|
||||
.unwrap(); // TODO: Error checking
|
||||
let res = client.request(request).await?;
|
||||
let body = res.into_body().try_concat().await?;
|
||||
let mut t = serde_json::from_slice::<AuthErrorOr<Token>>(&body)?.into_result()?;
|
||||
t.set_expiry_absolute();
|
||||
Ok(t)
|
||||
Token::from_json(&body)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the project root for licensing information.
|
||||
//
|
||||
use crate::authenticator_delegate::{DefaultInstalledFlowDelegate, InstalledFlowDelegate};
|
||||
use crate::error::{AuthErrorOr, Error};
|
||||
use crate::error::Error;
|
||||
use crate::types::{ApplicationSecret, Token};
|
||||
|
||||
use std::convert::AsRef;
|
||||
@@ -15,7 +15,6 @@ use std::sync::{Arc, Mutex};
|
||||
use futures::future::FutureExt;
|
||||
use futures_util::try_stream::TryStreamExt;
|
||||
use hyper::header;
|
||||
use serde::Deserialize;
|
||||
use tokio::sync::oneshot;
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
|
||||
@@ -187,30 +186,7 @@ impl InstalledFlow {
|
||||
let request = Self::request_token(app_secret, authcode, redirect_uri, server_addr);
|
||||
let resp = hyper_client.request(request).await?;
|
||||
let body = resp.into_body().try_concat().await?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct JSONTokenResponse {
|
||||
access_token: String,
|
||||
refresh_token: Option<String>,
|
||||
token_type: String,
|
||||
expires_in: Option<i64>,
|
||||
}
|
||||
|
||||
let JSONTokenResponse {
|
||||
access_token,
|
||||
refresh_token,
|
||||
token_type,
|
||||
expires_in,
|
||||
} = serde_json::from_slice::<AuthErrorOr<_>>(&body)?.into_result()?;
|
||||
let mut token = Token {
|
||||
access_token,
|
||||
refresh_token,
|
||||
token_type,
|
||||
expires_in,
|
||||
expires_in_timestamp: None,
|
||||
};
|
||||
token.set_expiry_absolute();
|
||||
Ok(token)
|
||||
Token::from_json(&body)
|
||||
}
|
||||
|
||||
/// Sends the authorization code to the provider in order to obtain access and refresh tokens.
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::error::{AuthErrorOr, Error};
|
||||
use crate::error::Error;
|
||||
use crate::types::{ApplicationSecret, Token};
|
||||
|
||||
use chrono::Utc;
|
||||
use futures_util::try_stream::TryStreamExt;
|
||||
use hyper::header;
|
||||
use serde::Deserialize;
|
||||
use url::form_urlencoded;
|
||||
|
||||
/// Implements the [OAuth2 Refresh Token Flow](https://developers.google.com/youtube/v3/guides/authentication#devices).
|
||||
@@ -50,26 +48,7 @@ impl RefreshFlow {
|
||||
|
||||
let resp = client.request(request).await?;
|
||||
let body = resp.into_body().try_concat().await?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct JsonToken {
|
||||
access_token: String,
|
||||
token_type: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
let JsonToken {
|
||||
access_token,
|
||||
token_type,
|
||||
expires_in,
|
||||
} = serde_json::from_slice::<AuthErrorOr<_>>(&body)?.into_result()?;
|
||||
Ok(Token {
|
||||
access_token,
|
||||
token_type,
|
||||
refresh_token: Some(refresh_token.to_string()),
|
||||
expires_in: None,
|
||||
expires_in_timestamp: Some(Utc::now().timestamp() + expires_in),
|
||||
})
|
||||
Token::from_json(&body)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
|
||||
//!
|
||||
|
||||
use crate::error::{AuthErrorOr, Error};
|
||||
use crate::error::Error;
|
||||
use crate::types::Token;
|
||||
|
||||
use std::io;
|
||||
@@ -202,28 +202,7 @@ impl ServiceAccountFlow {
|
||||
.unwrap();
|
||||
let response = hyper_client.request(request).await?;
|
||||
let body = response.into_body().try_concat().await?;
|
||||
|
||||
/// This is the schema of the server's response.
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct TokenResponse {
|
||||
access_token: String,
|
||||
token_type: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
let TokenResponse {
|
||||
access_token,
|
||||
token_type,
|
||||
expires_in,
|
||||
} = serde_json::from_slice::<AuthErrorOr<_>>(&body)?.into_result()?;
|
||||
let expires_ts = chrono::Utc::now().timestamp() + expires_in;
|
||||
Ok(Token {
|
||||
access_token,
|
||||
token_type,
|
||||
refresh_token: None,
|
||||
expires_in: Some(expires_in),
|
||||
expires_in_timestamp: Some(expires_ts),
|
||||
})
|
||||
Token::from_json(&body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +211,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::helper::read_service_account_key;
|
||||
use crate::parse_json;
|
||||
use chrono::Utc;
|
||||
use hyper_rustls::HttpsConnector;
|
||||
|
||||
use mockito::mock;
|
||||
@@ -263,8 +243,7 @@ mod tests {
|
||||
"token_type": "Bearer"
|
||||
});
|
||||
let bad_json_response = serde_json::json!({
|
||||
"access_token": "ya29.c.ElouBywiys0LyNaZoLPJcp1Fdi2KjFMxzvYKLXkTdvM-rDfqKlvEq6PiMhGoGHx97t5FAvz3eb_ahdwlBjSStxHtDVQB4ZPRJQ_EOi-iS7PnayahU2S9Jp8S6rk",
|
||||
"token_type": "Bearer"
|
||||
"error": "access_denied",
|
||||
});
|
||||
|
||||
// Successful path.
|
||||
@@ -285,7 +264,7 @@ mod tests {
|
||||
.await
|
||||
.expect("token failed");
|
||||
assert!(tok.access_token.contains("ya29.c.ElouBywiys0Ly"));
|
||||
assert_eq!(Some(3600), tok.expires_in);
|
||||
assert!(Utc::now() + chrono::Duration::seconds(3600) >= tok.expires_at.unwrap());
|
||||
_m.assert();
|
||||
}
|
||||
// Malformed response.
|
||||
|
||||
75
src/types.rs
75
src/types.rs
@@ -1,4 +1,6 @@
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use crate::error::{AuthErrorOr, Error};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a token as returned by OAuth2 servers.
|
||||
@@ -21,50 +23,43 @@ pub struct Token {
|
||||
pub refresh_token: Option<String>,
|
||||
/// The token type as string - usually 'Bearer'.
|
||||
pub token_type: String,
|
||||
/// access_token will expire after this amount of time.
|
||||
/// Prefer using expiry_date()
|
||||
pub expires_in: Option<i64>,
|
||||
/// timestamp is seconds since epoch indicating when the token will expire in absolute terms.
|
||||
/// use expiry_date() to convert to DateTime.
|
||||
pub expires_in_timestamp: Option<i64>,
|
||||
/// The time when the token expires.
|
||||
pub expires_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub(crate) fn from_json(json_data: &[u8]) -> Result<Token, 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()?;
|
||||
|
||||
let expires_at = expires_in
|
||||
.map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));
|
||||
|
||||
Ok(Token {
|
||||
access_token,
|
||||
refresh_token,
|
||||
token_type,
|
||||
expires_at,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if we are expired.
|
||||
///
|
||||
/// # Panics
|
||||
/// * if our access_token is unset
|
||||
pub fn expired(&self) -> bool {
|
||||
if self.access_token.is_empty() {
|
||||
panic!("called expired() on unset token");
|
||||
}
|
||||
if let Some(expiry_date) = self.expiry_date() {
|
||||
expiry_date - chrono::Duration::minutes(1) <= Utc::now()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a DateTime object representing our expiry date.
|
||||
pub fn expiry_date(&self) -> Option<DateTime<Utc>> {
|
||||
let expires_in_timestamp = self.expires_in_timestamp?;
|
||||
|
||||
Utc.timestamp(expires_in_timestamp, 0).into()
|
||||
}
|
||||
|
||||
/// Adjust our stored expiry format to be absolute, using the current time.
|
||||
pub fn set_expiry_absolute(&mut self) -> &mut Token {
|
||||
if self.expires_in_timestamp.is_some() {
|
||||
assert!(self.expires_in.is_none());
|
||||
return self;
|
||||
}
|
||||
|
||||
if let Some(expires_in) = self.expires_in {
|
||||
self.expires_in_timestamp = Some(Utc::now().timestamp() + expires_in);
|
||||
self.expires_in = None;
|
||||
}
|
||||
|
||||
self
|
||||
self.expires_at
|
||||
.map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user