diff --git a/src/config_default_credentials.rs b/src/config_default_credentials.rs index 6f078fb..10cb513 100644 --- a/src/config_default_credentials.rs +++ b/src/config_default_credentials.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::sync::Arc; use async_trait::async_trait; @@ -14,7 +15,10 @@ use crate::{Error, TokenProvider}; /// A token provider that uses the default user credentials /// -/// Reads credentials from `.config/gcloud/application_default_credentials.json`. +/// Reads credentials from `.config/gcloud/application_default_credentials.json` on Linux and MacOS +/// or from `%APPDATA%/gcloud/application_default_credentials.json` on Windows. +/// See [GCloud Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials#personal) +/// for details. #[derive(Debug)] pub struct ConfigDefaultCredentials { client: HttpClient, @@ -30,11 +34,12 @@ impl ConfigDefaultCredentials { } pub(crate) async fn with_client(client: &HttpClient) -> Result<Self, Error> { - debug!("try to load credentials from {}", USER_CREDENTIALS_PATH); - let mut home = home::home_dir().ok_or(Error::Str("home directory not found"))?; - home.push(USER_CREDENTIALS_PATH); + debug!("try to load credentials from configuration"); + let mut config_path = config_dir()?; + config_path.push(USER_CREDENTIALS_PATH); + debug!(config = config_path.to_str(), "reading configuration file"); - let credentials = AuthorizedUserRefreshToken::from_file(&home)?; + let credentials = AuthorizedUserRefreshToken::from_file(&config_path)?; debug!(project = ?credentials.quota_project_id, client = credentials.client_id, "found user credentials"); Ok(Self { @@ -103,5 +108,29 @@ struct RefreshRequest<'a> { refresh_token: &'a str, } +#[cfg(any(target_os = "linux", target_os = "macos"))] +fn config_dir() -> Result<PathBuf, Error> { + let mut home = home::home_dir().ok_or(Error::Str("home directory not found"))?; + home.push(CONFIG_DIR); + Ok(home) +} + +#[cfg(target_os = "windows")] +fn config_dir() -> Result<PathBuf, Error> { + let app_data = std::env::var(ENV_APPDATA) + .map_err(|_| Error::Str("APPDATA environment variable not found"))?; + let config_path = PathBuf::from(app_data); + match config_path.exists() { + true => Ok(config_path), + false => Err(Error::Str("APPDATA directory not found")), + } +} + const DEFAULT_TOKEN_GCP_URI: &str = "https://accounts.google.com/o/oauth2/token"; -const USER_CREDENTIALS_PATH: &str = ".config/gcloud/application_default_credentials.json"; +const USER_CREDENTIALS_PATH: &str = "gcloud/application_default_credentials.json"; + +#[cfg(any(target_os = "linux", target_os = "macos"))] +const CONFIG_DIR: &str = ".config"; + +#[cfg(target_os = "windows")] +const ENV_APPDATA: &str = "APPDATA";