diff --git a/examples/test-adc/src/main.rs b/examples/test-adc/src/main.rs index cac39c9..0b63f4f 100644 --- a/examples/test-adc/src/main.rs +++ b/examples/test-adc/src/main.rs @@ -1,9 +1,11 @@ use yup_oauth2::authenticator::ApplicationDefaultCredentialsTypes; use yup_oauth2::ApplicationDefaultCredentialsAuthenticator; +use yup_oauth2::ApplicationDefaultCredentialsFlowOpts; #[tokio::main] async fn main() { - let auth = match ApplicationDefaultCredentialsAuthenticator::builder().await { + let opts = ApplicationDefaultCredentialsFlowOpts::default(); + let auth = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await { ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth .build() .await diff --git a/src/application_default_credentials.rs b/src/application_default_credentials.rs index f052df2..5caa05c 100644 --- a/src/application_default_credentials.rs +++ b/src/application_default_credentials.rs @@ -1,13 +1,25 @@ use crate::error::Error; use crate::types::TokenInfo; -pub struct ApplicationDefaultCredentialsFlowOpts; +/// Provide options for the Application Default Credential Flow, mostly used for testing +pub struct ApplicationDefaultCredentialsFlowOpts { + /// Used as base to build the url during token request from GCP metadata server + pub metadata_url: Option, +} +impl Default for ApplicationDefaultCredentialsFlowOpts { + fn default() -> Self { + Self { metadata_url: None } + } +} + +pub struct ApplicationDefaultCredentialsFlow { + metadata_url: String, +} -/// ServiceAccountFlow can fetch oauth tokens using a service account. -pub struct ApplicationDefaultCredentialsFlow; impl ApplicationDefaultCredentialsFlow { - pub(crate) fn new(_opts: ApplicationDefaultCredentialsFlowOpts) -> Self { - ApplicationDefaultCredentialsFlow {} + pub(crate) fn new(opts: ApplicationDefaultCredentialsFlowOpts) -> Self { + let metadata_url = opts.metadata_url.unwrap_or_else(|| "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token".to_string()); + ApplicationDefaultCredentialsFlow { metadata_url } } pub(crate) async fn token( @@ -20,7 +32,7 @@ impl ApplicationDefaultCredentialsFlow { C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, { let scope = crate::helper::join(scopes, ","); - let token_uri = format!("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token?scopes={}", scope); + let token_uri = format!("{}?scopes={}", self.metadata_url, scope); // TODO: This feels jank, can it be done better? let request = hyper::Request::get(token_uri) .header("Metadata-Flavor", "Google") .body(hyper::Body::from(String::new())) // why body is needed? diff --git a/src/authenticator.rs b/src/authenticator.rs index ec2dc2b..2564650 100644 --- a/src/authenticator.rs +++ b/src/authenticator.rs @@ -274,14 +274,7 @@ impl ServiceAccountAuthenticator { /// ``` pub struct ApplicationDefaultCredentialsAuthenticator; impl ApplicationDefaultCredentialsAuthenticator { - /// Use modified builder pattern to create an Authenticator that uses GCE instance metadata server - /// to provide tokens. - pub fn from_instance_metadata() -> ApplicationDefaultCredentialsFlowOpts { - ApplicationDefaultCredentialsFlowOpts {} - } - - /// Use modified builder pattern to create an Authenticator that pulls default application credentials - /// service account file name from os environment variable. + /// Try to build ServiceAccountFlowOpts from the environment pub async fn from_environment() -> Result { let service_account_key = crate::read_service_account_key(std::env::var("GOOGLE_APPLICATION_CREDENTIALS")?) @@ -296,12 +289,17 @@ impl ApplicationDefaultCredentialsAuthenticator { /// Use the builder pattern to deduce which model of authenticator should be used: /// Service account one or GCE instance metadata kind - pub async fn builder() -> ApplicationDefaultCredentialsTypes { - Self::with_client(DefaultHyperClient).await + pub async fn builder( + opts: ApplicationDefaultCredentialsFlowOpts, + ) -> ApplicationDefaultCredentialsTypes { + Self::with_client(DefaultHyperClient, opts).await } /// Use the builder pattern to deduce which model of authenticator should be used and allow providing a hyper client - pub async fn with_client(client: C) -> ApplicationDefaultCredentialsTypes + pub async fn with_client( + client: C, + opts: ApplicationDefaultCredentialsFlowOpts, + ) -> ApplicationDefaultCredentialsTypes where C: HyperClientBuilder, { @@ -311,12 +309,9 @@ impl ApplicationDefaultCredentialsAuthenticator { ApplicationDefaultCredentialsTypes::ServiceAccount(builder) } - Err(_) => { - ApplicationDefaultCredentialsTypes::InstanceMetadata(AuthenticatorBuilder::new( - ApplicationDefaultCredentialsAuthenticator::from_instance_metadata(), - client, - )) - } + Err(_) => ApplicationDefaultCredentialsTypes::InstanceMetadata( + AuthenticatorBuilder::new(opts, client), + ), } } } diff --git a/src/lib.rs b/src/lib.rs index a82f6ff..f4c298a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,7 @@ pub use crate::authenticator::{ pub use crate::helper::*; pub use crate::installed::InstalledFlowReturnMethod; +pub use crate::application_default_credentials::ApplicationDefaultCredentialsFlowOpts; pub use crate::service_account::ServiceAccountKey; #[doc(inline)] diff --git a/tests/tests.rs b/tests/tests.rs index fab7e6e..0711bd7 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,9 +2,9 @@ use yup_oauth2::{ authenticator::{DefaultAuthenticator, DefaultHyperClient, HyperClientBuilder}, authenticator_delegate::{DeviceAuthResponse, DeviceFlowDelegate, InstalledFlowDelegate}, error::{AuthError, AuthErrorCode}, - ApplicationDefaultCredentialsAuthenticator, ApplicationSecret, DeviceFlowAuthenticator, Error, - InstalledFlowAuthenticator, InstalledFlowReturnMethod, ServiceAccountAuthenticator, - ServiceAccountKey, + ApplicationDefaultCredentialsAuthenticator, ApplicationDefaultCredentialsFlowOpts, + ApplicationSecret, DeviceFlowAuthenticator, Error, InstalledFlowAuthenticator, + InstalledFlowReturnMethod, ServiceAccountAuthenticator, ServiceAccountKey, }; use std::future::Future; @@ -619,7 +619,11 @@ async fn test_default_application_credentials_from_metadata_server() { "expires_in": 12345678, }))), ); - let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder().await { + + let opts = ApplicationDefaultCredentialsFlowOpts { + metadata_url: Some(server.url("/token").to_string()), + }; + let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await { ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth.build().await.unwrap(), _ => panic!("We are not testing service account adc model"), }; @@ -627,5 +631,5 @@ async fn test_default_application_credentials_from_metadata_server() { .token(&["https://googleapis.com/some/scope"]) .await .unwrap(); - assert_ne!(token.as_str(), "accesstoken"); + assert_eq!(token.as_str(), "accesstoken"); }