diff --git a/sdk/identity/src/token_credentials/app_service_managed_identity_credential.rs b/sdk/identity/src/token_credentials/app_service_managed_identity_credential.rs new file mode 100644 index 0000000000..c48fcab291 --- /dev/null +++ b/sdk/identity/src/token_credentials/app_service_managed_identity_credential.rs @@ -0,0 +1,42 @@ +use crate::{ImdsManagedIdentityClient, TokenCredentialOptions}; +use azure_core::auth::{AccessToken, TokenCredential}; +use azure_core::headers::HeaderName; +use azure_core::Url; + +const ENDPOINT_ENV: &str = "IDENTITY_ENDPOINT"; +const API_VERSION: &str = "2019-08-01"; +const SECRET_HEADER: HeaderName = HeaderName::from_static("x-identity-header"); +const SECRET_ENV: &str = "IDENTITY_HEADER"; + +#[derive(Debug)] +pub struct AppServiceManagedIdentityCredential { + client: ImdsManagedIdentityClient, +} + +impl AppServiceManagedIdentityCredential { + pub fn create(options: TokenCredentialOptions) -> azure_core::Result { + let env = options.env(); + let endpoint = Url::parse(&env.var(ENDPOINT_ENV)?)?; + Ok(Self { + client: ImdsManagedIdentityClient::new( + options, + endpoint, + API_VERSION, + SECRET_HEADER, + SECRET_ENV, + ), + }) + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl TokenCredential for AppServiceManagedIdentityCredential { + async fn get_token(&self, scopes: &[&str]) -> azure_core::Result { + self.client.get_token(scopes).await + } + + async fn clear_cache(&self) -> azure_core::Result<()> { + self.client.clear_cache().await + } +} diff --git a/sdk/identity/src/token_credentials/imds_managed_identity_credential.rs b/sdk/identity/src/token_credentials/imds_managed_identity_credential.rs index 2649caf7c5..d7cbddf1f5 100644 --- a/sdk/identity/src/token_credentials/imds_managed_identity_credential.rs +++ b/sdk/identity/src/token_credentials/imds_managed_identity_credential.rs @@ -1,8 +1,10 @@ use crate::{token_credentials::cache::TokenCache, TokenCredentialOptions}; use azure_core::{ auth::{AccessToken, Secret, TokenCredential}, - error::{Error, ErrorKind, ResultExt}, - from_json, HttpClient, Method, Request, StatusCode, Url, + error::{Error, ErrorKind}, + from_json, + headers::HeaderName, + HttpClient, Method, Request, StatusCode, Url, }; use serde::{ de::{self, Deserializer}, @@ -11,9 +13,10 @@ use serde::{ use std::{str, sync::Arc}; use time::OffsetDateTime; -const MSI_ENDPOINT_ENV_KEY: &str = "IDENTITY_ENDPOINT"; -const MSI_SECRET_ENV_KEY: &str = "IDENTITY_HEADER"; -const MSI_API_VERSION: &str = "2019-08-01"; +const ENDPOINT: &str = "http://169.254.169.254/metadata/identity/oauth2/token"; +const API_VERSION: &str = "2019-08-01"; +const SECRET_HEADER: HeaderName = HeaderName::from_static("x-identity-header"); +const SECRET_ENV: &str = "IDENTITY_HEADER"; /// Attempts authentication using a managed identity that has been assigned to the deployment environment. /// @@ -21,8 +24,12 @@ const MSI_API_VERSION: &str = "2019-08-01"; /// /// Built up from docs at [https://docs.microsoft.com/azure/app-service/overview-managed-identity#using-the-rest-protocol](https://docs.microsoft.com/azure/app-service/overview-managed-identity#using-the-rest-protocol) #[derive(Debug)] -pub struct ImdsManagedIdentityClient { +pub(crate) struct ImdsManagedIdentityClient { http_client: Arc, + endpoint: Url, + api_version: String, + secret_header: HeaderName, + secret_env: String, object_id: Option, client_id: Option, msi_res_id: Option, @@ -36,18 +43,19 @@ pub struct ImdsManagedIdentityCredential { impl ImdsManagedIdentityCredential { pub fn new(options: TokenCredentialOptions) -> Self { + let endpoint = Url::parse(ENDPOINT).unwrap(); // valid url constant Self { - client: ImdsManagedIdentityClient::new(options), + client: ImdsManagedIdentityClient::new( + options, + endpoint, + API_VERSION, + SECRET_HEADER, + SECRET_ENV, + ), } } } -impl Default for ImdsManagedIdentityCredential { - fn default() -> Self { - Self::new(TokenCredentialOptions::default()) - } -} - #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TokenCredential for ImdsManagedIdentityCredential { @@ -61,9 +69,19 @@ impl TokenCredential for ImdsManagedIdentityCredential { } impl ImdsManagedIdentityClient { - pub fn new(options: TokenCredentialOptions) -> Self { + pub fn new( + options: TokenCredentialOptions, + endpoint: Url, + api_version: &str, + secret_header: HeaderName, + secret_env: &str, + ) -> Self { Self { http_client: options.http_client(), + endpoint, + api_version: api_version.to_owned(), + secret_header: secret_header.to_owned(), + secret_env: secret_env.to_owned(), object_id: None, client_id: None, msi_res_id: None, @@ -116,10 +134,10 @@ impl ImdsManagedIdentityClient { async fn get_token(&self, scopes: &[&str]) -> azure_core::Result { let resource = scopes_to_resource(scopes)?; - let msi_endpoint = std::env::var(MSI_ENDPOINT_ENV_KEY) - .unwrap_or_else(|_| "http://169.254.169.254/metadata/identity/oauth2/token".to_owned()); - - let mut query_items = vec![("api-version", MSI_API_VERSION), ("resource", &resource)]; + let mut query_items = vec![ + ("api-version", self.api_version.as_str()), + ("resource", resource), + ]; match ( self.object_id.as_ref(), @@ -132,18 +150,16 @@ impl ImdsManagedIdentityClient { _ => (), } - let url = Url::parse_with_params(&msi_endpoint, &query_items).context( - ErrorKind::DataConversion, - "error parsing url for MSI endpoint", - )?; + let mut url = self.endpoint.clone(); + url.query_pairs_mut().extend_pairs(query_items); let mut req = Request::new(url, Method::Get); req.insert_header("metadata", "true"); - let msi_secret = std::env::var(MSI_SECRET_ENV_KEY); + let msi_secret = std::env::var(&self.secret_env); if let Ok(val) = msi_secret { - req.insert_header("x-identity-header", val); + req.insert_header(self.secret_header.clone(), val); }; let rsp = self.http_client.execute_request(&req).await?; diff --git a/sdk/identity/src/token_credentials/mod.rs b/sdk/identity/src/token_credentials/mod.rs index 2dd857c926..2f68c918aa 100644 --- a/sdk/identity/src/token_credentials/mod.rs +++ b/sdk/identity/src/token_credentials/mod.rs @@ -5,6 +5,7 @@ //! * Azure CLI credentials cache //! * Managed identity //! * Client secret +mod app_service_managed_identity_credential; mod azure_cli_credential; #[cfg(feature = "azureauth-cli")] mod azureauth_cli_credential; @@ -19,6 +20,7 @@ mod options; mod specific_azure_credential; mod workload_identity_credential; +pub use app_service_managed_identity_credential::*; pub use azure_cli_credential::*; #[cfg(feature = "azureauth-cli")] pub use azureauth_cli_credential::*;