From 383ac09cab50395eac6630399d32b18a264071fb Mon Sep 17 00:00:00 2001 From: Charles Lowell <10964656+chlowell@users.noreply.github.com> Date: Tue, 13 May 2025 20:18:19 +0000 Subject: [PATCH 1/2] Delete azure_identity dead code --- .../src/chained_token_credential.rs | 239 ------------------ .../azure_identity/src/credentials/cache.rs | 7 - sdk/identity/azure_identity/src/lib.rs | 2 - .../azure_identity/src/refresh_token.rs | 163 ------------ 4 files changed, 411 deletions(-) delete mode 100644 sdk/identity/azure_identity/src/chained_token_credential.rs delete mode 100644 sdk/identity/azure_identity/src/refresh_token.rs diff --git a/sdk/identity/azure_identity/src/chained_token_credential.rs b/sdk/identity/azure_identity/src/chained_token_credential.rs deleted file mode 100644 index 1598ae71cd..0000000000 --- a/sdk/identity/azure_identity/src/chained_token_credential.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#![allow(dead_code)] - -use crate::credentials::cache::TokenCache; -use crate::TokenCredentialOptions; -use async_lock::RwLock; -use azure_core::{ - credentials::{AccessToken, TokenCredential}, - error::{Error, ErrorKind}, -}; -use std::sync::Arc; - -#[derive(Debug, Default)] -/// ChainedTokenCredentialOptions contains optional parameters for ChainedTokenCredential. -pub struct ChainedTokenCredentialOptions { - pub retry_sources: bool, - pub credential_options: TokenCredentialOptions, -} - -// TODO: Should probably remove this once we consolidate and unify credentials. -impl From for ChainedTokenCredentialOptions { - fn from(credential_options: TokenCredentialOptions) -> Self { - Self { - retry_sources: Default::default(), - credential_options, - } - } -} - -/// Provides a user-configurable `TokenCredential` authentication flow for applications that will be deployed to Azure. -/// -/// The credential types are tried in the order specified by the user. -#[derive(Debug)] -pub struct ChainedTokenCredential { - #[allow(dead_code)] - options: ChainedTokenCredentialOptions, - sources: Vec>, - cache: TokenCache, - successful_credential: RwLock>>, -} - -impl ChainedTokenCredential { - /// Create a `ChainedTokenCredential` with options. - pub fn new(options: Option) -> Self { - Self { - options: options.unwrap_or_default(), - sources: Vec::new(), - cache: TokenCache::new(), - successful_credential: RwLock::new(None), - } - } - - /// Add a credential source to the chain. - pub fn add_source(&mut self, source: Arc) { - self.sources.push(source); - } - - async fn get_token_impl( - &self, - scopes: &[&str], - ) -> azure_core::Result<(Arc, AccessToken)> { - let mut errors = Vec::new(); - for source in &self.sources { - let token_res = source.get_token(scopes).await; - - match token_res { - Ok(token) => return Ok((source.clone(), token)), - Err(error) => errors.push(error), - } - } - Err(Error::with_message(ErrorKind::Credential, || { - format!( - "Multiple errors were encountered while attempting to authenticate:\n{}", - format_aggregate_error(&errors) - ) - })) - } - - /// Try to fetch a token using each of the credential sources until one succeeds - async fn get_token(&self, scopes: &[&str]) -> azure_core::Result { - if !self.options.retry_sources { - if let Some(entry) = self.successful_credential.read().await.as_ref() { - return entry.get_token(scopes).await; - } - let mut lock = self.successful_credential.write().await; - // if after getting the write lock, we find that another thread has already found a credential, use that. - if let Some(entry) = lock.as_ref() { - return entry.get_token(scopes).await; - } - let (entry, token) = self.get_token_impl(scopes).await?; - *lock = Some(entry); - Ok(token) - } else { - // if we are retrying sources, we don't need to cache the successful credential - Ok(self.get_token_impl(scopes).await?.1) - } - } -} - -impl From<&[Arc]> for ChainedTokenCredential { - fn from(credential_options: &[Arc]) -> Self { - Self { - options: ChainedTokenCredentialOptions::default(), - sources: credential_options.to_vec(), - cache: TokenCache::new(), - successful_credential: RwLock::new(None), - } - } -} - -#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl TokenCredential for ChainedTokenCredential { - async fn get_token(&self, scopes: &[&str]) -> azure_core::Result { - self.cache.get_token(scopes, self.get_token(scopes)).await - } -} - -fn format_aggregate_error(errors: &[Error]) -> String { - use std::error::Error; - errors - .iter() - .map(|e| { - let mut current: Option<&dyn Error> = Some(e); - let mut stack = vec![]; - while let Some(err) = current.take() { - stack.push(err.to_string()); - current = err.source(); - } - stack.join(" - ") - }) - .collect::>() - .join("\n") -} - -#[cfg(test)] -mod tests { - use super::*; - use async_lock::Mutex; - use azure_core::credentials::{AccessToken, TokenCredential}; - use azure_core_test::credentials::MockCredential; - - /// `TokenFailure` is a mock credential that always fails to get a token. - #[derive(Debug)] - struct TokenFailure { - counter: Mutex, - } - - impl TokenFailure { - fn new() -> Self { - Self { - counter: Mutex::new(0), - } - } - - async fn get_counter(&self) -> u32 { - let count = self.counter.lock().await; - *count - } - } - - #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] - impl TokenCredential for TokenFailure { - async fn get_token(&self, _scopes: &[&str]) -> azure_core::Result { - let mut count = self.counter.lock().await; - *count += 1; - Err(Error::message(ErrorKind::Credential, "failed to get token")) - } - } - - #[tokio::test] - async fn test_basic() -> azure_core::Result<()> { - let providers: Vec> = vec![Arc::new(MockCredential {})]; - let credentials = ChainedTokenCredential::from(providers.as_slice()); - let scopes = ["https://management.azure.com/.default"]; - let token = credentials.get_token(&scopes).await?; - assert_eq!( - token.token.secret(), - "TEST TOKEN https://management.azure.com/.default" - ); - - Ok(()) - } - - #[tokio::test] - async fn test_with_retry() -> azure_core::Result<()> { - let token_failure = Arc::new(TokenFailure::new()); - let mut chained_credential = - ChainedTokenCredential::new(Some(ChainedTokenCredentialOptions { - retry_sources: true, - ..Default::default() - })); - chained_credential.add_source(token_failure.clone()); - chained_credential.add_source(Arc::new(MockCredential {})); - - let scopes = ["https://management.azure.com/.default"]; - let token = chained_credential.get_token(&scopes).await?; - assert_eq!( - token.token.secret(), - "TEST TOKEN https://management.azure.com/.default" - ); - let scopes = ["https://management.azure.com/.default"]; - let token = chained_credential.get_token(&scopes).await?; - assert_eq!( - token.token.secret(), - "TEST TOKEN https://management.azure.com/.default" - ); - - assert_eq!(token_failure.get_counter().await, 2); - Ok(()) - } - - #[tokio::test] - async fn test_without_retry() -> azure_core::Result<()> { - let token_failure = Arc::new(TokenFailure::new()); - let mut chained_credential = ChainedTokenCredential::new(None); - chained_credential.add_source(token_failure.clone()); - chained_credential.add_source(Arc::new(MockCredential {})); - - let scopes = ["https://management.azure.com/.default"]; - let token = chained_credential.get_token(&scopes).await?; - assert_eq!( - token.token.secret(), - "TEST TOKEN https://management.azure.com/.default" - ); - let scopes = ["https://management.azure.com/.default"]; - let token = chained_credential.get_token(&scopes).await?; - assert_eq!( - token.token.secret(), - "TEST TOKEN https://management.azure.com/.default" - ); - - assert_eq!(token_failure.get_counter().await, 1); - Ok(()) - } -} diff --git a/sdk/identity/azure_identity/src/credentials/cache.rs b/sdk/identity/azure_identity/src/credentials/cache.rs index 20fd9a1b22..e18c96adfd 100644 --- a/sdk/identity/azure_identity/src/credentials/cache.rs +++ b/sdk/identity/azure_identity/src/credentials/cache.rs @@ -15,13 +15,6 @@ impl TokenCache { Self(RwLock::new(HashMap::new())) } - #[allow(dead_code)] - pub(crate) async fn clear(&self) -> azure_core::Result<()> { - let mut token_cache = self.0.write().await; - token_cache.clear(); - Ok(()) - } - pub(crate) async fn get_token( &self, scopes: &[&str], diff --git a/sdk/identity/azure_identity/src/lib.rs b/sdk/identity/azure_identity/src/lib.rs index f991109cfc..ddd50045f8 100644 --- a/sdk/identity/azure_identity/src/lib.rs +++ b/sdk/identity/azure_identity/src/lib.rs @@ -7,13 +7,11 @@ mod authorization_code_flow; mod azure_developer_cli_credential; mod azure_pipelines_credential; -mod chained_token_credential; mod client_secret_credential; mod credentials; mod env; mod managed_identity_credential; mod oauth2_http_client; -mod refresh_token; use azure_core::{ error::{ErrorKind, ResultExt}, diff --git a/sdk/identity/azure_identity/src/refresh_token.rs b/sdk/identity/azure_identity/src/refresh_token.rs deleted file mode 100644 index 46a63e7619..0000000000 --- a/sdk/identity/azure_identity/src/refresh_token.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -//! Refresh tokens. - -use azure_core::{ - credentials::Secret, - error::{http_response_from_body, Error, ErrorKind, ResultExt}, - http::{ - headers::{self, content_type}, - request::Request, - HttpClient, Method, Url, - }, - json::from_json, -}; -use serde::Deserialize; -use std::fmt; -use std::sync::Arc; -use url::form_urlencoded; - -/// Exchange a refresh token for a new access token and refresh token. -#[allow(dead_code)] -pub async fn exchange( - http_client: Arc, - tenant_id: &str, - client_id: &str, - client_secret: Option<&str>, - refresh_token: &Secret, -) -> azure_core::Result { - let encoded = { - let mut encoded = &mut form_urlencoded::Serializer::new(String::new()); - encoded = encoded - .append_pair("grant_type", "refresh_token") - .append_pair("client_id", client_id) - .append_pair("refresh_token", refresh_token.secret()); - // optionally add the client secret - if let Some(client_secret) = client_secret { - encoded = encoded.append_pair("client_secret", client_secret); - }; - encoded.finish() - }; - - let url = Url::parse(&format!( - "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" - ))?; - - let mut req = Request::new(url, Method::Post); - req.insert_header( - headers::CONTENT_TYPE, - content_type::APPLICATION_X_WWW_FORM_URLENCODED, - ); - req.set_body(encoded); - - let rsp = http_client.execute_request(&req).await?; - let rsp_status = rsp.status(); - - if rsp_status.is_success() { - rsp.into_json_body().await.map_kind(ErrorKind::Credential) - } else { - let rsp_body = rsp.into_raw_body().collect().await?; - let token_error: RefreshTokenError = - from_json(&rsp_body).map_err(|_| http_response_from_body(rsp_status, &rsp_body))?; - Err(Error::new(ErrorKind::Credential, token_error)) - } -} - -/// A refresh token -#[allow(dead_code)] -#[derive(Debug, Clone, Deserialize)] -pub struct RefreshTokenResponse { - token_type: String, - #[serde(rename = "scope", deserialize_with = "deserialize::split")] - scopes: Vec, - expires_in: u64, - ext_expires_in: u64, - access_token: Secret, - refresh_token: Secret, -} - -#[allow(dead_code)] -impl RefreshTokenResponse { - /// Returns the `token_type`. Always `Bearer` for Azure AD. - pub fn token_type(&self) -> &str { - &self.token_type - } - /// The scopes that the `access_token` is valid for. - pub fn scopes(&self) -> &[String] { - &self.scopes - } - /// Number of seconds the `access_token` is valid for. - pub fn expires_in(&self) -> u64 { - self.expires_in - } - /// Issued for the scopes that were requested. - pub fn access_token(&self) -> &Secret { - &self.access_token - } - /// The new refresh token and should replace old refresh token. - pub fn refresh_token(&self) -> &Secret { - &self.refresh_token - } - /// Indicates the extended lifetime of an `access_token`. - pub fn ext_expires_in(&self) -> u64 { - self.ext_expires_in - } -} - -mod deserialize { - use serde::Deserializer; - pub fn split<'de, D>(scope: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let string: String = serde::Deserialize::deserialize(scope)?; - Ok(string.split(' ').map(ToOwned::to_owned).collect()) - } -} - -// cspell:ignore suberror - -/// An error response body when there is an error requesting a token -#[derive(Debug, Clone, Deserialize)] -#[allow(unused)] -pub struct RefreshTokenError { - error: String, - error_description: String, - error_codes: Vec, - timestamp: Option, - trace_id: Option, - correlation_id: Option, - suberror: Option, - claims: Option, -} - -impl std::error::Error for RefreshTokenError {} - -impl fmt::Display for RefreshTokenError { - fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> { - writeln!(f, "error: {}", self.error)?; - if let Some(suberror) = &self.suberror { - writeln!(f, "suberror: {suberror}")?; - } - writeln!(f, "description: {}", self.error_description) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn require_send(_t: T) {} - - #[test] - fn ensure_that_exchange_is_send() { - require_send(exchange( - azure_core::http::new_http_client(), - "UNUSED", - "UNUSED", - None, - &Secret::new("UNUSED"), - )); - } -} From 3b8da3bb78159fa0bb06234c4aa04899927b02ec Mon Sep 17 00:00:00 2001 From: Charles Lowell <10964656+chlowell@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:58:46 +0000 Subject: [PATCH 2/2] Remove oauth2 dependency --- Cargo.lock | 93 -------------- Cargo.toml | 1 - eng/dict/crates.txt | 1 - sdk/identity/azure_identity/Cargo.toml | 1 - .../src/authorization_code_flow.rs | 115 ----------------- sdk/identity/azure_identity/src/lib.rs | 2 - .../azure_identity/src/oauth2_http_client.rs | 116 ------------------ 7 files changed, 329 deletions(-) delete mode 100644 sdk/identity/azure_identity/src/authorization_code_flow.rs delete mode 100644 sdk/identity/azure_identity/src/oauth2_http_client.rs diff --git a/Cargo.lock b/Cargo.lock index b453225aac..c89a43460c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,21 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anes" version = "0.1.6" @@ -290,7 +275,6 @@ dependencies = [ "azure_security_keyvault_secrets", "clap", "futures", - "oauth2", "openssl", "pin-project", "reqwest", @@ -551,21 +535,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets", -] - [[package]] name = "ciborium" version = "0.2.2" @@ -1328,29 +1297,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -1707,25 +1653,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "oauth2" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" -dependencies = [ - "base64", - "chrono", - "getrandom", - "http", - "rand", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror", - "url", -] - [[package]] name = "object" version = "0.36.5" @@ -2392,16 +2319,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.19" @@ -2941,7 +2858,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] [[package]] @@ -3136,15 +3052,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-registry" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 70e5520ff7..f1c583bdf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,6 @@ getrandom = { version = "0.2", features = ["js"] } hmac = { version = "0.12" } litemap = "0.7.4" log = "0.4" -oauth2 = { version = "5.0.0", default-features = false } openssl = { version = "0.10.72" } pin-project = "1.0" proc-macro2 = "1.0.86" diff --git a/eng/dict/crates.txt b/eng/dict/crates.txt index 8510057ed7..01bc133f8a 100644 --- a/eng/dict/crates.txt +++ b/eng/dict/crates.txt @@ -35,7 +35,6 @@ getrandom hmac litemap log -oauth2 openssl pin-project proc-macro2 diff --git a/sdk/identity/azure_identity/Cargo.toml b/sdk/identity/azure_identity/Cargo.toml index cd73361993..f48b609255 100644 --- a/sdk/identity/azure_identity/Cargo.toml +++ b/sdk/identity/azure_identity/Cargo.toml @@ -17,7 +17,6 @@ async-lock.workspace = true async-trait.workspace = true azure_core.workspace = true futures.workspace = true -oauth2.workspace = true openssl = { workspace = true, optional = true } pin-project.workspace = true serde.workspace = true diff --git a/sdk/identity/azure_identity/src/authorization_code_flow.rs b/sdk/identity/azure_identity/src/authorization_code_flow.rs deleted file mode 100644 index 747b59d3af..0000000000 --- a/sdk/identity/azure_identity/src/authorization_code_flow.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -//! Authorize using the authorization code flow -//! -//! You can learn more about the `OAuth2` authorization code flow [here](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow). - -#![allow(dead_code)] - -use crate::oauth2_http_client::Oauth2HttpClient; -use azure_core::{ - error::{ErrorKind, ResultExt}, - http::{HttpClient, Url}, -}; -use oauth2::{basic::BasicClient, EndpointNotSet, EndpointSet, HttpRequest, Scope}; -use oauth2::{ClientId, ClientSecret}; -use std::sync::Arc; - -/// Start an authorization code flow. -/// -/// The values for `client_id`, `client_secret`, `tenant_id`, and `redirect_url` can all be found -/// inside of the Azure portal. -#[allow(dead_code)] -pub fn authorize( - client_id: ClientId, - client_secret: Option, - tenant_id: &str, - redirect_url: Url, - scopes: &[&str], -) -> AuthorizationCodeFlow { - let auth_url = oauth2::AuthUrl::from_url( - Url::parse(&format!( - "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize" - )) - .expect("Invalid authorization endpoint URL"), - ); - let token_url = oauth2::TokenUrl::from_url( - Url::parse(&format!( - "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" - )) - .expect("Invalid token endpoint URL"), - ); - - // Set up the config for the Microsoft Graph OAuth2 process. - let mut client = BasicClient::new(client_id) - .set_auth_uri(auth_url) - .set_token_uri(token_url) - // Microsoft Graph requires client_id and client_secret in URL rather than - // using Basic authentication. - .set_auth_type(oauth2::AuthType::RequestBody) - .set_redirect_uri(oauth2::RedirectUrl::from_url(redirect_url)); - - if let Some(client_secret) = client_secret { - client = client.set_client_secret(client_secret); - } - - // Microsoft Graph supports Proof Key for Code Exchange (PKCE - https://oauth.net/2/pkce/). - // Create a PKCE code verifier and SHA-256 encode it as a code challenge. - let (pkce_code_challenge, pkce_code_verifier) = oauth2::PkceCodeChallenge::new_random_sha256(); - - let scopes = scopes.iter().map(ToString::to_string).map(Scope::new); - - // Generate the authorization URL to which we'll redirect the user. - let (authorize_url, csrf_state) = client - .authorize_url(oauth2::CsrfToken::new_random) - .add_scopes(scopes) - .set_pkce_challenge(pkce_code_challenge) - .url(); - - AuthorizationCodeFlow { - client, - authorize_url, - csrf_state, - pkce_code_verifier, - } -} - -/// An object representing an OAuth 2.0 authorization code flow. -#[derive(Debug)] -pub struct AuthorizationCodeFlow { - /// An HTTP client configured for OAuth2 authentication - pub client: - BasicClient, - /// The authentication HTTP endpoint - pub authorize_url: Url, - /// The CSRF token - pub csrf_state: oauth2::CsrfToken, - /// The PKCE code verifier - pub pkce_code_verifier: oauth2::PkceCodeVerifier, -} - -#[allow(dead_code)] -impl AuthorizationCodeFlow { - /// Exchange an authorization code for a token. - pub async fn exchange( - self, - http_client: Arc, - code: oauth2::AuthorizationCode, - ) -> azure_core::Result< - oauth2::StandardTokenResponse, - > { - let oauth_http_client = Oauth2HttpClient::new(http_client.clone()); - let client = |request: HttpRequest| oauth_http_client.request(request); - self.client - .exchange_code(code) - // Send the PKCE code verifier in the token request - .set_pkce_verifier(self.pkce_code_verifier) - .request_async(&client) - .await - .context( - ErrorKind::Credential, - "exchanging an authorization code for a token failed", - ) - } -} diff --git a/sdk/identity/azure_identity/src/lib.rs b/sdk/identity/azure_identity/src/lib.rs index ddd50045f8..589ac3a803 100644 --- a/sdk/identity/azure_identity/src/lib.rs +++ b/sdk/identity/azure_identity/src/lib.rs @@ -4,14 +4,12 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -mod authorization_code_flow; mod azure_developer_cli_credential; mod azure_pipelines_credential; mod client_secret_credential; mod credentials; mod env; mod managed_identity_credential; -mod oauth2_http_client; use azure_core::{ error::{ErrorKind, ResultExt}, diff --git a/sdk/identity/azure_identity/src/oauth2_http_client.rs b/sdk/identity/azure_identity/src/oauth2_http_client.rs deleted file mode 100644 index 9a2c27d838..0000000000 --- a/sdk/identity/azure_identity/src/oauth2_http_client.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -//! Implements the oauth2 crate http client interface using an [`azure_core::HttpClient`] instance. -//! - -use azure_core::{ - error::{Error, ErrorKind, ResultExt}, - http::{ - headers, - request::{Body, Request}, - HttpClient, Method, StatusCode, Url, - }, - Bytes, -}; -use std::{collections::HashMap, str::FromStr, sync::Arc}; -use tracing::warn; - -pub(crate) struct Oauth2HttpClient { - http_client: Arc, -} - -impl Oauth2HttpClient { - /// Create a new `Oauth2HttpClient`. - pub fn new(http_client: Arc) -> Self { - Self { http_client } - } - - pub(crate) async fn request( - &self, - oauth2_request: oauth2::HttpRequest, - ) -> Result { - let method = try_from_method(oauth2_request.method())?; - let url: Url = oauth2_request.uri().to_string().parse().map_err(|e| { - Error::full( - ErrorKind::Other, - e, - "Failed to parse the http::Uri as a url::Url", - ) - })?; - let mut request = Request::new(url, method); - for (name, value) in to_headers(oauth2_request.headers()) { - request.insert_header(name, value); - } - request.set_body(Body::Bytes(Bytes::copy_from_slice( - oauth2_request.body().as_slice(), - ))); - let response = self.http_client.execute_request(&request).await?; - let status_code = try_from_status(response.status())?; - let headers = try_from_headers(response.headers())?; - let body = response.into_raw_body().collect().await?.to_vec(); - - let mut oauth_response = oauth2::HttpResponse::new(body); - *oauth_response.headers_mut() = headers; - *oauth_response.status_mut() = status_code; - - Ok(oauth_response) - } -} - -fn try_from_method(method: &oauth2::http::Method) -> azure_core::Result { - match *method { - oauth2::http::Method::GET => Ok(Method::Get), - oauth2::http::Method::POST => Ok(Method::Post), - oauth2::http::Method::PUT => Ok(Method::Put), - oauth2::http::Method::DELETE => Ok(Method::Delete), - oauth2::http::Method::HEAD => Ok(Method::Head), - oauth2::http::Method::PATCH => Ok(Method::Patch), - _ => Err(Error::with_message(ErrorKind::DataConversion, || { - format!("unsupported oauth2::http::Method {method}") - })), - } -} - -fn try_from_headers(headers: &headers::Headers) -> azure_core::Result { - let mut header_map = oauth2::http::HeaderMap::new(); - for (name, value) in headers.iter() { - let name = name.as_str(); - let header_name = oauth2::http::header::HeaderName::from_str(name) - .with_context(ErrorKind::DataConversion, || { - format!("unable to convert http header name '{name}'") - })?; - let value = value.as_str().to_owned(); - header_map.append( - header_name, - oauth2::http::HeaderValue::from_str(&value) - .with_context(ErrorKind::DataConversion, || { - format!("unable to convert http header value for '{name}'") - })?, - ); - } - Ok(header_map) -} - -fn try_from_status(status: StatusCode) -> azure_core::Result { - oauth2::http::StatusCode::from_u16(*status).map_kind(ErrorKind::DataConversion) -} - -fn to_headers(map: &oauth2::http::header::HeaderMap) -> headers::Headers { - let map = map - .iter() - .filter_map(|(k, v)| { - let key = k.as_str(); - if let Ok(value) = v.to_str() { - Some(( - headers::HeaderName::from(key.to_owned()), - headers::HeaderValue::from(value.to_owned()), - )) - } else { - warn!("header value for `{key}` is not utf8"); - None - } - }) - .collect::>(); - headers::Headers::from(map) -}