From 57e1f826184fcbaa52d2b2db44f1f97d1efbd9e0 Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 25 Sep 2025 18:03:43 +0200 Subject: [PATCH 1/3] Add support for setting Bitwarden-Client-Name and Bitwarden-Client-Version --- .../bitwarden-sm/src/client_secrets.rs | 1 + .../bitwarden-auth/src/send_access/client.rs | 1 + crates/bitwarden-core/src/client/client.rs | 26 +++++++- .../src/client/client_settings.rs | 63 +++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/client_secrets.rs b/bitwarden_license/bitwarden-sm/src/client_secrets.rs index 92d7350b2..ed0128792 100644 --- a/bitwarden_license/bitwarden-sm/src/client_secrets.rs +++ b/bitwarden_license/bitwarden-sm/src/client_secrets.rs @@ -138,6 +138,7 @@ mod tests { api_url: format!("http://{}/api", server.address()), user_agent: "Bitwarden Rust-SDK [TEST]".into(), device_type: DeviceType::SDK, + bitwarden_client_version: 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 3b295e0f7..ca3247d76 100644 --- a/crates/bitwarden-auth/src/send_access/client.rs +++ b/crates/bitwarden-auth/src/send_access/client.rs @@ -121,6 +121,7 @@ mod tests { api_url: format!("http://{}/api", mock_server.address()), user_agent: "Bitwarden Rust-SDK [TEST]".into(), device_type: DeviceType::SDK, + bitwarden_client_version: 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 63403f128..1be9f6b38 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -8,9 +8,12 @@ use reqwest::header::{self, HeaderValue}; use super::internal::InternalClient; #[cfg(feature = "internal")] use crate::client::flags::Flags; -use crate::client::{ - client_settings::ClientSettings, - internal::{ApiConfigurations, ClientManagedTokens, SdkManagedTokens, Tokens}, +use crate::{ + client::{ + client_settings::{ClientName, ClientSettings}, + internal::{ApiConfigurations, ClientManagedTokens, SdkManagedTokens, Tokens}, + }, + DeviceType, }; /// The main struct to interact with the Bitwarden SDK. @@ -72,6 +75,23 @@ impl Client { HeaderValue::from_str(&(settings.device_type as u8).to_string()) .expect("All numbers are valid ASCII"), ); + + let client_name: Option = settings.device_type.into(); + if let Some(device_type) = client_name { + headers.append( + "Bitwarden-Client-Name", + HeaderValue::from_str(&device_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 client_builder = new_client_builder().default_headers(headers); let client = client_builder.build().expect("Build should not fail"); diff --git a/crates/bitwarden-core/src/client/client_settings.rs b/crates/bitwarden-core/src/client/client_settings.rs index cc495ea91..f17293bc5 100644 --- a/crates/bitwarden-core/src/client/client_settings.rs +++ b/crates/bitwarden-core/src/client/client_settings.rs @@ -1,3 +1,5 @@ +use std::fmt; + use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -13,6 +15,7 @@ use serde::{Deserialize, Serialize}; /// api_url: "https://api.bitwarden.com".to_string(), /// user_agent: "Bitwarden Rust-SDK".to_string(), /// device_type: DeviceType::SDK, +/// bitwarden_client_version: None, /// }; /// let default = ClientSettings::default(); /// ``` @@ -33,6 +36,8 @@ 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. + pub bitwarden_client_version: Option, } impl Default for ClientSettings { @@ -42,6 +47,7 @@ impl Default for ClientSettings { api_url: "https://api.bitwarden.com".into(), user_agent: "Bitwarden Rust-SDK".into(), device_type: DeviceType::SDK, + bitwarden_client_version: None, } } } @@ -81,4 +87,61 @@ pub enum DeviceType { WindowsCLI = 23, MacOsCLI = 24, LinuxCLI = 25, + DuckDuckGoBrowser = 26, +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum ClientName { + Web, + Browser, + Desktop, + Cli, +} + +impl From for Option { + fn from(device_type: DeviceType) -> Self { + match device_type { + DeviceType::Android | DeviceType::AndroidAmazon | DeviceType::iOS => None, + + DeviceType::ChromeBrowser + | DeviceType::FirefoxBrowser + | DeviceType::OperaBrowser + | DeviceType::EdgeBrowser + | DeviceType::IEBrowser + | DeviceType::SafariBrowser + | DeviceType::VivaldiBrowser + | DeviceType::DuckDuckGoBrowser + | DeviceType::UnknownBrowser => Some(ClientName::Web), + + DeviceType::ChromeExtension + | DeviceType::FirefoxExtension + | DeviceType::OperaExtension + | DeviceType::EdgeExtension + | DeviceType::VivaldiExtension + | DeviceType::SafariExtension => Some(ClientName::Browser), + + DeviceType::LinuxDesktop + | DeviceType::MacOsDesktop + | DeviceType::WindowsDesktop + | DeviceType::UWP => Some(ClientName::Desktop), + + DeviceType::WindowsCLI | DeviceType::MacOsCLI | DeviceType::LinuxCLI => { + Some(ClientName::Cli) + } + + DeviceType::SDK | DeviceType::Server => None, + } + } +} + +impl fmt::Display for ClientName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + ClientName::Web => "web", + ClientName::Browser => "browser", + ClientName::Desktop => "desktop", + ClientName::Cli => "cli", + }; + write!(f, "{}", s) + } } From c2db723fb24bb5f013f9846019f19d49597ba254 Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 25 Sep 2025 18:14:47 +0200 Subject: [PATCH 2/3] Add mobile --- crates/bitwarden-core/src/client/client_settings.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/client/client_settings.rs b/crates/bitwarden-core/src/client/client_settings.rs index f17293bc5..f42ecba4d 100644 --- a/crates/bitwarden-core/src/client/client_settings.rs +++ b/crates/bitwarden-core/src/client/client_settings.rs @@ -95,13 +95,16 @@ pub(crate) enum ClientName { Web, Browser, Desktop, + Mobile, Cli, } impl From for Option { fn from(device_type: DeviceType) -> Self { match device_type { - DeviceType::Android | DeviceType::AndroidAmazon | DeviceType::iOS => None, + DeviceType::Android | DeviceType::AndroidAmazon | DeviceType::iOS => { + Some(ClientName::Mobile) + } DeviceType::ChromeBrowser | DeviceType::FirefoxBrowser @@ -140,6 +143,7 @@ impl fmt::Display for ClientName { ClientName::Web => "web", ClientName::Browser => "browser", ClientName::Desktop => "desktop", + ClientName::Mobile => "mobile", ClientName::Cli => "cli", }; write!(f, "{}", s) From 6f3ad8ce6b3138c818ced1282979935e73d26857 Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 25 Sep 2025 18:32:33 +0200 Subject: [PATCH 3/3] Simplify --- crates/bitwarden-core/src/client/client.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/bitwarden-core/src/client/client.rs b/crates/bitwarden-core/src/client/client.rs index 1be9f6b38..5f1de80de 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -8,12 +8,9 @@ use reqwest::header::{self, HeaderValue}; use super::internal::InternalClient; #[cfg(feature = "internal")] use crate::client::flags::Flags; -use crate::{ - client::{ - client_settings::{ClientName, ClientSettings}, - internal::{ApiConfigurations, ClientManagedTokens, SdkManagedTokens, Tokens}, - }, - DeviceType, +use crate::client::{ + client_settings::{ClientName, ClientSettings}, + internal::{ApiConfigurations, ClientManagedTokens, SdkManagedTokens, Tokens}, }; /// The main struct to interact with the Bitwarden SDK. @@ -76,11 +73,10 @@ impl Client { .expect("All numbers are valid ASCII"), ); - let client_name: Option = settings.device_type.into(); - if let Some(device_type) = client_name { + if let Some(client_type) = Into::>::into(settings.device_type) { headers.append( "Bitwarden-Client-Name", - HeaderValue::from_str(&device_type.to_string()) + HeaderValue::from_str(&client_type.to_string()) .expect("All ASCII strings are valid header values"), ); }