diff --git a/Cargo.toml b/Cargo.toml index 89d7886..82535c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ required-features = ["hyper-rustls"] name = "tests" required-features = ["hyper-rustls"] - [features] default = ["hyper-rustls"] @@ -47,6 +46,7 @@ httptest = "0.14" env_logger = "0.7" tempfile = "3.1" webbrowser = "0.5" +hyper-rustls = "0.22.1" [workspace] members = ["examples/test-installed/", "examples/test-svc-acct/", "examples/test-device/", "examples/service_account", "examples/drive_example"] diff --git a/examples/custom_client.rs b/examples/custom_client.rs new file mode 100644 index 0000000..3294cc2 --- /dev/null +++ b/examples/custom_client.rs @@ -0,0 +1,43 @@ +//! This example demonstrates how to use the same connection pool for both obtaining the tokens and +//! using the API that these tokens authorize. In most cases e.g. obtaining the service account key +//! will already establish a keep-alive http connection, so the succeeding API call should be much +//! faster. +//! +//! It is also a better use of resources (memory, sockets, etc.) + +async fn r#use( + client: hyper::Client, + authenticator: yup_oauth2::authenticator::Authenticator, +) -> Result<(), Box> +where + C: Clone + Send + Sync + hyper::client::connect::Connect + 'static, +{ + let token = authenticator.token(&["email"]).await?; + let request = http::Request::get("https://example.com") + .header( + http::header::AUTHORIZATION, + format!("Bearer {}", token.as_str()), + ) + .body(hyper::body::Body::empty())?; + let response = client.request(request).await?; + drop(response); // Implementing handling of the response is left as an exercise for the reader. + Ok(()) +} + +#[tokio::main] +async fn main() { + let google_credentials = std::env::var("GOOGLE_APPLICATION_CREDENTIALS") + .expect("env var GOOGLE_APPLICATION_CREDENTIALS is required"); + let secret = yup_oauth2::read_service_account_key(google_credentials) + .await + .expect("$GOOGLE_APPLICATION_CREDENTIALS is not a valid service account key"); + let client = hyper::Client::builder().build(hyper_rustls::HttpsConnector::with_native_roots()); + let authenticator = + yup_oauth2::ServiceAccountAuthenticator::with_client(secret, client.clone()) + .build() + .await + .expect("could not create an authenticator"); + r#use(client, authenticator) + .await + .expect("use is successful!"); +} diff --git a/src/authenticator.rs b/src/authenticator.rs index 4da0fdb..c7cb3a2 100644 --- a/src/authenticator.rs +++ b/src/authenticator.rs @@ -132,11 +132,6 @@ where } } -enum StorageType { - Memory, - Disk(PathBuf), -} - /// Configure an Authenticator using the builder pattern. pub struct AuthenticatorBuilder { hyper_client_builder: C, @@ -171,9 +166,16 @@ impl InstalledFlowAuthenticator { app_secret: ApplicationSecret, method: InstalledFlowReturnMethod, ) -> AuthenticatorBuilder { - AuthenticatorBuilder::::with_auth_flow(InstalledFlow::new( - app_secret, method, - )) + Self::with_client(app_secret, method, DefaultHyperClient) + } + + /// Construct a new Authenticator that uses the installed flow and the provided http client. + pub fn with_client( + app_secret: ApplicationSecret, + method: InstalledFlowReturnMethod, + client: C, + ) -> AuthenticatorBuilder { + AuthenticatorBuilder::new(InstalledFlow::new(app_secret, method), client) } } @@ -198,7 +200,15 @@ impl DeviceFlowAuthenticator { pub fn builder( app_secret: ApplicationSecret, ) -> AuthenticatorBuilder { - AuthenticatorBuilder::::with_auth_flow(DeviceFlow::new(app_secret)) + Self::with_client(app_secret, DefaultHyperClient) + } + + /// Construct a new Authenticator that uses the installed flow and the provided http client. + pub fn with_client( + app_secret: ApplicationSecret, + client: C, + ) -> AuthenticatorBuilder { + AuthenticatorBuilder::new(DeviceFlow::new(app_secret), client) } } @@ -223,10 +233,21 @@ impl ServiceAccountAuthenticator { pub fn builder( service_account_key: ServiceAccountKey, ) -> AuthenticatorBuilder { - AuthenticatorBuilder::::with_auth_flow(ServiceAccountFlowOpts { - key: service_account_key, - subject: None, - }) + Self::with_client(service_account_key, DefaultHyperClient) + } + + /// Construct a new Authenticator that uses the installed flow and the provided http client. + pub fn with_client( + service_account_key: ServiceAccountKey, + client: C, + ) -> AuthenticatorBuilder { + AuthenticatorBuilder::new( + ServiceAccountFlowOpts { + key: service_account_key, + subject: None, + }, + client, + ) } } @@ -270,14 +291,9 @@ impl AuthenticatorBuilder { }) } - #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))] - #[cfg_attr( - yup_oauth2_docsrs, - doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))) - )] - fn with_auth_flow(auth_flow: F) -> AuthenticatorBuilder { + fn new(auth_flow: F, hyper_client_builder: C) -> AuthenticatorBuilder { AuthenticatorBuilder { - hyper_client_builder: DefaultHyperClient, + hyper_client_builder, storage_type: StorageType::Memory, auth_flow, } @@ -509,17 +525,6 @@ pub trait HyperClientBuilder { fn build_hyper_client(self) -> hyper::Client; } -impl HyperClientBuilder for hyper::Client -where - C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, -{ - type Connector = C; - - fn build_hyper_client(self) -> hyper::Client { - self - } -} - #[cfg(feature = "hyper-rustls")] #[cfg_attr( yup_oauth2_docsrs, diff --git a/src/service_account.rs b/src/service_account.rs index 4dbf8ad..1c11863 100644 --- a/src/service_account.rs +++ b/src/service_account.rs @@ -211,9 +211,7 @@ impl ServiceAccountFlow { #[cfg(test)] mod tests { - use super::*; - use crate::authenticator::HyperClientBuilder; use crate::helper::read_service_account_key; // Valid but deactivated key. @@ -222,13 +220,13 @@ mod tests { // Uncomment this test to verify that we can successfully obtain tokens. //#[tokio::test] #[allow(dead_code)] - #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))] async fn test_service_account_e2e() { let key = read_service_account_key(TEST_PRIVATE_KEY_PATH) .await .unwrap(); let acc = ServiceAccountFlow::new(ServiceAccountFlowOpts { key, subject: None }).unwrap(); - let client = crate::authenticator::DefaultHyperClient.build_hyper_client(); + let client = + hyper::Client::builder().build(hyper_rustls::HttpsConnector::with_native_roots()); println!( "{:?}", acc.token(&client, &["https://www.googleapis.com/auth/pubsub"])