diff --git a/bitwarden_license/bitwarden-sm/src/client_secrets.rs b/bitwarden_license/bitwarden-sm/src/client_secrets.rs index 794bda32f..a3a253047 100644 --- a/bitwarden_license/bitwarden-sm/src/client_secrets.rs +++ b/bitwarden_license/bitwarden-sm/src/client_secrets.rs @@ -137,7 +137,9 @@ mod tests { api_url: format!("http://{}/api", server.address()), user_agent: "Bitwarden Rust-SDK [TEST]".into(), device_type: DeviceType::SDK, + device_identifier: None, bitwarden_client_version: None, + bitwarden_package_type: None, }; (server, Client::new(Some(settings))) diff --git a/crates/bitwarden-auth/src/send_access/client.rs b/crates/bitwarden-auth/src/send_access/client.rs index 78499abe3..f0cfc4e24 100644 --- a/crates/bitwarden-auth/src/send_access/client.rs +++ b/crates/bitwarden-auth/src/send_access/client.rs @@ -124,7 +124,9 @@ mod tests { api_url: format!("http://{}/api", mock_server.address()), user_agent: "Bitwarden Rust-SDK [TEST]".into(), device_type: DeviceType::SDK, + device_identifier: None, bitwarden_client_version: None, + bitwarden_package_type: None, }; let core_client = CoreClient::new(Some(settings)); core_client.auth_new().send_access() diff --git a/crates/bitwarden-core/src/client/client.rs b/crates/bitwarden-core/src/client/client.rs index 9720f1bb1..04e619232 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -41,61 +41,21 @@ impl Client { fn new_internal(settings_input: Option, tokens: Tokens) -> Self { let settings = settings_input.unwrap_or_default(); - fn new_client_builder() -> reqwest::ClientBuilder { - #[allow(unused_mut)] - let mut client_builder = reqwest::Client::builder(); - - #[cfg(not(target_arch = "wasm32"))] - { - use rustls::ClientConfig; - use rustls_platform_verifier::ConfigVerifierExt; - client_builder = client_builder.use_preconfigured_tls( - ClientConfig::with_platform_verifier() - .expect("Failed to create platform verifier"), - ); - - // Enforce HTTPS for all requests in non-debug builds - #[cfg(not(debug_assertions))] - { - client_builder = client_builder.https_only(true); - } - } - - client_builder - } - - let external_client = new_client_builder().build().expect("Build should not fail"); - - let mut headers = header::HeaderMap::new(); - headers.append( - "Device-Type", - HeaderValue::from_str(&(settings.device_type as u8).to_string()) - .expect("All numbers are valid ASCII"), - ); + let external_http_client = new_http_client_builder() + .build() + .expect("External HTTP Client build should not fail"); - if let Some(client_type) = Into::>::into(settings.device_type) { - headers.append( - "Bitwarden-Client-Name", - HeaderValue::from_str(&client_type.to_string()) - .expect("All ASCII strings are valid header values"), - ); - } - - if let Some(version) = &settings.bitwarden_client_version { - headers.append( - "Bitwarden-Client-Version", - HeaderValue::from_str(version).expect("Version should be a valid header value"), - ); - } + let headers = build_default_headers(&settings); - let client_builder = new_client_builder().default_headers(headers); - - let client = client_builder.build().expect("Build should not fail"); + let bw_http_client = new_http_client_builder() + .default_headers(headers) + .build() + .expect("Bw HTTP Client build should not fail"); let identity = bitwarden_api_identity::apis::configuration::Configuration { base_path: settings.identity_url, user_agent: Some(settings.user_agent.clone()), - client: client.clone(), + client: bw_http_client.clone(), basic_auth: None, oauth_access_token: None, bearer_access_token: None, @@ -105,7 +65,7 @@ impl Client { let api = bitwarden_api_api::apis::configuration::Configuration { base_path: settings.api_url, user_agent: Some(settings.user_agent), - client, + client: bw_http_client, basic_auth: None, oauth_access_token: None, bearer_access_token: None, @@ -124,7 +84,7 @@ impl Client { api, settings.device_type, )), - external_client, + external_http_client, key_store: KeyStore::default(), #[cfg(feature = "internal")] security_state: RwLock::new(None), @@ -134,3 +94,81 @@ impl Client { } } } + +fn new_http_client_builder() -> reqwest::ClientBuilder { + #[allow(unused_mut)] + let mut client_builder = reqwest::Client::builder(); + + #[cfg(not(target_arch = "wasm32"))] + { + use rustls::ClientConfig; + use rustls_platform_verifier::ConfigVerifierExt; + client_builder = client_builder.use_preconfigured_tls( + ClientConfig::with_platform_verifier().expect("Failed to create platform verifier"), + ); + + // Enforce HTTPS for all requests in non-debug builds + #[cfg(not(debug_assertions))] + { + client_builder = client_builder.https_only(true); + } + } + + client_builder +} + +/// Build default headers for Bitwarden HttpClient +fn build_default_headers(settings: &ClientSettings) -> header::HeaderMap { + let mut headers = header::HeaderMap::new(); + + // Handle optional headers + + if let Some(device_identifier) = &settings.device_identifier { + headers.append( + "Device-Identifier", + HeaderValue::from_str(device_identifier) + .expect("Device identifier should be a valid header value"), + ); + } + + if let Some(client_type) = Into::>::into(settings.device_type) { + headers.append( + "Bitwarden-Client-Name", + HeaderValue::from_str(&client_type.to_string()) + .expect("All ASCII strings are valid header values"), + ); + } + + if let Some(version) = &settings.bitwarden_client_version { + headers.append( + "Bitwarden-Client-Version", + HeaderValue::from_str(version).expect("Version should be a valid header value"), + ); + } + + if let Some(package_type) = &settings.bitwarden_package_type { + headers.append( + "Bitwarden-Package-Type", + HeaderValue::from_str(package_type) + .expect("Package type should be a valid header value"), + ); + } + + // Handle required headers + + headers.append( + "Device-Type", + HeaderValue::from_str(&(settings.device_type as u8).to_string()) + .expect("All numbers are valid ASCII"), + ); + + // TODO: PM-29938 - Since we now add this header always, we need to remove this from the + // auto-generated code + headers.append( + reqwest::header::USER_AGENT, + HeaderValue::from_str(&settings.user_agent) + .expect("User agent should be a valid header value"), + ); + + headers +} diff --git a/crates/bitwarden-core/src/client/client_settings.rs b/crates/bitwarden-core/src/client/client_settings.rs index f42ecba4d..f6a7fc34c 100644 --- a/crates/bitwarden-core/src/client/client_settings.rs +++ b/crates/bitwarden-core/src/client/client_settings.rs @@ -16,6 +16,8 @@ use serde::{Deserialize, Serialize}; /// user_agent: "Bitwarden Rust-SDK".to_string(), /// device_type: DeviceType::SDK, /// bitwarden_client_version: None, +/// bitwarden_package_type: None, +/// device_identifier: None, /// }; /// let default = ClientSettings::default(); /// ``` @@ -36,8 +38,15 @@ pub struct ClientSettings { pub user_agent: String, /// Device type to send to Bitwarden. Defaults to SDK pub device_type: DeviceType, - /// Bitwarden Client Version to send to Bitwarden. + + // TODO: PM-29939 - Remove optionality when all clients pass these values + /// Device identifier to send to Bitwarden. Optional for now in transition period. + pub device_identifier: Option, + /// Bitwarden Client Version to send to Bitwarden. Optional for now in transition period. pub bitwarden_client_version: Option, + /// Bitwarden Package Type to send to Bitwarden. We should evaluate this field to see if it + /// should be optional later. + pub bitwarden_package_type: Option, } impl Default for ClientSettings { @@ -47,7 +56,9 @@ impl Default for ClientSettings { api_url: "https://api.bitwarden.com".into(), user_agent: "Bitwarden Rust-SDK".into(), device_type: DeviceType::SDK, + device_identifier: None, bitwarden_client_version: None, + bitwarden_package_type: None, } } } diff --git a/crates/bitwarden-core/src/client/internal.rs b/crates/bitwarden-core/src/client/internal.rs index 73d5d0aee..74fe8307d 100644 --- a/crates/bitwarden-core/src/client/internal.rs +++ b/crates/bitwarden-core/src/client/internal.rs @@ -125,7 +125,7 @@ pub struct InternalClient { /// Reqwest client useable for external integrations like email forwarders, HIBP. #[allow(unused)] - pub(crate) external_client: reqwest::Client, + pub(crate) external_http_client: reqwest::Client, pub(super) key_store: KeyStore, #[cfg(feature = "internal")] @@ -230,7 +230,7 @@ impl InternalClient { #[allow(missing_docs)] #[cfg(feature = "internal")] pub fn get_http_client(&self) -> &reqwest::Client { - &self.external_client + &self.external_http_client } #[allow(missing_docs)] diff --git a/crates/bitwarden-vault/src/cipher/cipher_client/share_cipher.rs b/crates/bitwarden-vault/src/cipher/cipher_client/share_cipher.rs index c51c53bc2..ed377b4e1 100644 --- a/crates/bitwarden-vault/src/cipher/cipher_client/share_cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher_client/share_cipher.rs @@ -795,7 +795,9 @@ mod tests { api_url: format!("http://{}", mock_server.address()), user_agent: "Bitwarden Test".into(), device_type: DeviceType::SDK, + device_identifier: None, bitwarden_client_version: None, + bitwarden_package_type: None, }; let client = Client::new(Some(settings)); diff --git a/crates/bw/src/vault/sync.rs b/crates/bw/src/vault/sync.rs index 2aa8ff34d..b7b8107e8 100644 --- a/crates/bw/src/vault/sync.rs +++ b/crates/bw/src/vault/sync.rs @@ -256,7 +256,9 @@ mod tests { api_url: api_config.base_path, user_agent: api_config.user_agent.unwrap(), device_type: DeviceType::SDK, + device_identifier: None, bitwarden_client_version: None, + bitwarden_package_type: None, })); client