Skip to content

Commit

Permalink
Remove tokio and reqwesrt
Browse files Browse the repository at this point in the history
  • Loading branch information
durch committed Jun 17, 2024
1 parent 34f48be commit 25fa9f7
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 121 deletions.
15 changes: 5 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "goauth"
version = "0.16.0"
version = "0.17.0-alpha.1"
authors = ["Drazen Urch <github@drazenur.ch>"]
description = "Crate for authenticating Server to Server Apps for Google Cloud Engine."
repository = "https://github.com/durch/rust-goauth"
Expand All @@ -16,23 +16,18 @@ path = "src/lib.rs"

[dependencies]
arc-swap = "1"
serde = "^1"
serde = "1"
serde_derive = "1"
serde_json = "1"
time = "0.3"
log = "0.4"
smpl_jwt = {version = "0.8.0", default-features = false}
reqwest = { version = "0.12", features = ["blocking", "json"], default-features = false }
futures = "0.3"
smpl_jwt = { version = "0.8.0", default-features = false }
attohttpc = { version = "0.28", features = ["json", "form"] }
simpl = "0.1"
tokio = { version = "1", features = ["rt-multi-thread"] }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
doc-comment = "0.3"
mockito = "0.31"

[features]
default = ["reqwest/default-tls"]
vendored-ssl = ["smpl_jwt/vendored", "reqwest/native-tls-vendored"]

vendored-ssl = ["smpl_jwt/vendored"]
52 changes: 21 additions & 31 deletions src/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::credentials::Credentials;
use crate::{get_token_with_client_and_body, Result};

use arc_swap::ArcSwapOption;
use reqwest::Client;
use smpl_jwt::Jwt;
use std::sync::{Arc, Mutex};
use time::{Duration, OffsetDateTime};
Expand All @@ -20,7 +19,6 @@ use time::{Duration, OffsetDateTime};
/// is within the `refresh_buffer` window, it will fetch a new token, store
/// that (along with the new expired time), and return the new token.
pub struct TokenFetcher {
client: Client,
jwt: Arc<Mutex<Jwt<JwtClaims>>>,
credentials: Credentials,
token_state: ArcSwapOption<TokenState>,
Expand All @@ -40,24 +38,17 @@ impl TokenFetcher {
credentials: Credentials,
refresh_buffer_seconds: i64,
) -> TokenFetcher {
TokenFetcher::with_client(
Client::new(),
jwt,
credentials,
Duration::new(refresh_buffer_seconds, 0),
)
TokenFetcher::with_client(jwt, credentials, Duration::new(refresh_buffer_seconds, 0))
}

pub fn with_client(
client: Client,
jwt: Jwt<JwtClaims>,
credentials: Credentials,
refresh_buffer: Duration,
) -> TokenFetcher {
let token_state = ArcSwapOption::from(None);

TokenFetcher {
client,
jwt: Arc::new(Mutex::new(jwt)),
credentials,
token_state,
Expand All @@ -69,19 +60,19 @@ impl TokenFetcher {
/// currently stored token's `expires_in` field and the configured
/// `refresh_buffer`. If it is, return the stored token. If not,
/// fetch a new token, store it, and return the new token.
pub async fn fetch_token(&self) -> Result<Token> {
pub fn fetch_token(&self) -> Result<Token> {
let token_state = self.token_state.load();

match &*token_state {
// First time calling `fetch_token` since initialization, so fetch
// a token.
None => self.get_token().await,
None => self.get_token(),
Some(token_state) => {
let now = OffsetDateTime::now_utc();

if now >= token_state.refresh_at {
// We have an existing token but it is time to refresh it
self.get_token().await
self.get_token()
} else {
// We have an existing, valid token, so return immediately
Ok(token_state.token.clone())
Expand All @@ -91,11 +82,10 @@ impl TokenFetcher {
}

/// Refresh the token
async fn get_token(&self) -> Result<Token> {
fn get_token(&self) -> Result<Token> {
let now = OffsetDateTime::now_utc();
let jwt_body = self.get_jwt_body(now)?;
let token =
get_token_with_client_and_body(&self.client, jwt_body, &self.credentials).await?;
let token = get_token_with_client_and_body(jwt_body, &self.credentials)?;
let expires_in = Duration::new(token.expires_in().into(), 0);

assert!(
Expand Down Expand Up @@ -168,8 +158,8 @@ mod tests {
(token, json.to_string())
}

#[tokio::test]
async fn basic_token_fetch() {
#[test]
fn basic_token_fetch() {
let (jwt, credentials) = get_mocks();

let refresh_buffer = 0;
Expand All @@ -179,12 +169,12 @@ mod tests {

let _mock = mock("POST", "/").with_status(200).with_body(json).create();

let token = fetcher.fetch_token().await.unwrap();
let token = fetcher.fetch_token().unwrap();
assert_eq!(expected_token, token);
}

#[tokio::test]
async fn basic_token_refresh() {
#[test]
fn basic_token_refresh() {
let (jwt, credentials) = get_mocks();

let refresh_buffer = 0;
Expand All @@ -200,19 +190,19 @@ mod tests {
.create();

// this should work
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

// sleep for `expires_in`
thread::sleep(StdDuration::from_secs(expires_in.into()));

// this should refresh
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

mock.assert();
}

#[tokio::test]
async fn token_refresh_with_buffer() {
#[test]
fn token_refresh_with_buffer() {
let (jwt, credentials) = get_mocks();

let refresh_buffer = 4;
Expand All @@ -228,20 +218,20 @@ mod tests {
.create();

// this should work
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

// sleep for `expires_in`
let sleep_for = expires_in - (refresh_buffer as u32);
thread::sleep(StdDuration::from_secs(sleep_for.into()));

// this should refresh
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

mock.assert();
}

#[tokio::test]
async fn doesnt_token_refresh_unnecessarily() {
#[test]
fn doesnt_token_refresh_unnecessarily() {
let (jwt, credentials) = get_mocks();

let refresh_buffer = 0;
Expand All @@ -257,10 +247,10 @@ mod tests {
.create();

// this should work
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

// fetch again, should not refresh
fetcher.fetch_token().await.unwrap();
fetcher.fetch_token().unwrap();

mock.assert();
}
Expand Down
96 changes: 16 additions & 80 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ pub mod scopes;
use auth::{JwtClaims, Token};
use credentials::Credentials;

use reqwest::Client;
pub use smpl_jwt::Jwt;
use std::str::FromStr;

use tokio::runtime::Runtime;

const DEFAULT_URL: &str = "https://www.googleapis.com/oauth2/v4/token";

fn form_body(body: &str) -> Vec<(&str, &str)> {
Expand All @@ -33,7 +30,7 @@ simpl::err!(GoErr,
Io@std::io::Error;
Jwt@smpl_jwt::JwtErr;
Json@serde_json::Error;
Reqwest@reqwest::Error;
Reqwest@attohttpc::Error;
Token@auth::TokenErr;
});

Expand Down Expand Up @@ -74,12 +71,10 @@ simpl::err!(GoErr,
/// ```
#[allow(clippy::result_large_err)]
pub fn get_token_legacy(jwt: &Jwt<JwtClaims>, url: Option<&str>) -> Result<Token> {
let client = reqwest::blocking::Client::new();
let final_jwt = jwt.finalize()?;
let request_body = form_body(&final_jwt);
let response = client
.post(url.unwrap_or(DEFAULT_URL))
.form(&request_body)
let response = attohttpc::post(url.unwrap_or(DEFAULT_URL))
.form(&request_body)?
.send()?;

Token::from_str(&response.text()?)
Expand All @@ -90,69 +85,21 @@ pub fn get_token_as_string_legacy(jwt: &Jwt<JwtClaims>, url: Option<&str>) -> Re
Ok(serde_json::to_string(&get_token_legacy(jwt, url)?)?)
}

#[allow(clippy::result_large_err)]
pub fn get_token_as_string(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Result<String> {
Ok(serde_json::to_string(&get_token_blocking(
jwt,
credentials,
)?)?)
}

/// Get Token which can be used to authenticate further request
/// ### Example
///
/// ```
/// extern crate smpl_jwt;
/// extern crate goauth;
/// #[macro_use]
/// extern crate log;
///
/// use goauth::auth::JwtClaims;
/// use goauth::scopes::Scope;
/// use goauth::credentials::Credentials;
/// use goauth::get_token_blocking;
/// use smpl_jwt::Jwt;
///
/// fn main() {
///
/// let credentials = Credentials::from_file("dummy_credentials_file_for_tests.json").unwrap();
///
/// let claims = JwtClaims::new(credentials.iss(),
/// &[Scope::DevStorageReadWrite],
/// credentials.token_uri(),
/// None, None);
///
/// let jwt = Jwt::new(claims, credentials.rsa_key().unwrap(), None);
/// match get_token_blocking(&jwt, &credentials) {
/// Ok(x) => debug!("{}", x),
/// Err(e) => debug!("{}", e)
/// };
/// }
///
/// ```
#[allow(clippy::result_large_err)]
pub fn get_token_blocking(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Result<Token> {
let rt = Runtime::new()?;
rt.block_on(get_token(jwt, credentials))
}

/// Async get Token which can be used to authenticate further request
/// ### Example
///
/// ```
/// ```rust no_run
/// extern crate smpl_jwt;
/// extern crate goauth;
/// #[macro_use]
/// extern crate log;
/// extern crate futures;
///
/// use goauth::auth::JwtClaims;
/// use goauth::scopes::Scope;
/// use goauth::credentials::Credentials;
/// use goauth::GoErr;
/// use goauth::get_token;
/// use smpl_jwt::Jwt;
/// use futures::future::Future;
///
///
/// fn main() -> Result<(), GoErr> {
Expand All @@ -165,50 +112,39 @@ pub fn get_token_blocking(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Re
/// None, None);
///
/// let jwt = Jwt::new(claims, credentials.rsa_key().unwrap(), None);
/// async {
/// match get_token(&jwt, &credentials).await {
/// match get_token(&jwt, &credentials) {
/// Ok(token) => println!("{}", token),
/// Err(_) => panic!("An error occurred, somewhere in there, try debugging with `get_token_with_creds`")
/// }
/// Err(e) => panic!("{}", e)
/// };
/// Ok(())
/// }
///
/// ```
pub async fn get_token(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Result<Token> {
let client = Client::new();

get_token_with_client(&client, jwt, credentials).await
pub fn get_token(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Result<Token> {
get_token_with_client(jwt, credentials)
}

pub async fn get_token_with_client(
client: &Client,
jwt: &Jwt<JwtClaims>,
credentials: &Credentials,
) -> Result<Token> {
pub fn get_token_with_client(jwt: &Jwt<JwtClaims>, credentials: &Credentials) -> Result<Token> {
let jwt_body = jwt.finalize()?;

get_token_with_client_and_body(client, jwt_body, credentials).await
get_token_with_client_and_body(jwt_body, credentials)
}

pub(crate) async fn get_token_with_client_and_body(
client: &Client,
pub(crate) fn get_token_with_client_and_body(
jwt_body: String,
credentials: &Credentials,
) -> Result<Token> {
let request_body = form_body(&jwt_body);

let response = client
.post(&credentials.token_uri())
.form(&request_body)
.send()
.await?;
let response = attohttpc::post(credentials.token_uri())
.form(&request_body)?
.send()?;

if response.status().is_success() {
let token = response.json::<Token>().await?;
let token = response.json::<Token>()?;
Ok(token)
} else {
let token_err = response.json::<auth::TokenErr>().await?;
let token_err = response.json::<auth::TokenErr>()?;
Err(GoErr::from(token_err))
}
}
Expand Down

0 comments on commit 25fa9f7

Please sign in to comment.