bitwarden_core/client/
client.rs1use std::sync::{Arc, OnceLock, RwLock};
2
3use bitwarden_crypto::KeyStore;
4#[cfg(feature = "internal")]
5use bitwarden_state::registry::StateRegistry;
6use reqwest::header::{self, HeaderValue};
7
8use super::internal::InternalClient;
9#[cfg(feature = "internal")]
10use crate::client::flags::Flags;
11use crate::{
12 auth::auth_tokens::{NoopTokenHandler, TokenHandler},
13 client::{
14 client_settings::{ClientName, ClientSettings},
15 internal::ApiConfigurations,
16 },
17};
18
19#[derive(Clone)]
21pub struct Client {
22 #[doc(hidden)]
27 pub internal: Arc<InternalClient>,
28}
29
30impl Client {
31 pub fn new(settings: Option<ClientSettings>) -> Self {
33 Self::new_internal(settings, Arc::new(NoopTokenHandler))
34 }
35
36 pub fn new_with_token_handler(
39 settings: Option<ClientSettings>,
40 token_handler: Arc<dyn TokenHandler>,
41 ) -> Self {
42 Self::new_internal(settings, token_handler)
43 }
44
45 fn new_internal(
46 settings_input: Option<ClientSettings>,
47 token_handler: Arc<dyn TokenHandler>,
48 ) -> Self {
49 let settings = settings_input.unwrap_or_default();
50
51 let external_http_client = new_http_client_builder()
52 .build()
53 .expect("External HTTP Client build should not fail");
54
55 let headers = build_default_headers(&settings);
56
57 let login_method = Arc::new(RwLock::new(None));
58 let key_store = KeyStore::default();
59
60 let bw_http_client = new_http_client_builder()
62 .default_headers(headers)
63 .build()
64 .expect("Bw HTTP Client build should not fail");
65 let identity = bitwarden_api_identity::Configuration {
66 base_path: settings.identity_url,
67 user_agent: Some(settings.user_agent.clone()),
68 client: bw_http_client.clone().into(),
69 oauth_access_token: None,
70 };
71
72 let auth_middleware = token_handler.initialize_middleware(
74 login_method.clone(),
75 identity.clone(),
76 key_store.clone(),
77 );
78 let bw_http_client = reqwest_middleware::ClientBuilder::new(bw_http_client)
79 .with_arc(auth_middleware)
80 .build();
81 let api = bitwarden_api_api::Configuration {
82 base_path: settings.api_url,
83 user_agent: Some(settings.user_agent),
84 client: bw_http_client,
85 oauth_access_token: None,
86 };
87
88 Self {
89 internal: Arc::new(InternalClient {
90 user_id: OnceLock::new(),
91 token_handler,
92 login_method,
93 #[cfg(feature = "internal")]
94 flags: RwLock::new(Flags::default()),
95 api_configurations: ApiConfigurations::new(identity, api, settings.device_type),
96 external_http_client,
97 key_store,
98 #[cfg(feature = "internal")]
99 security_state: RwLock::new(None),
100 #[cfg(feature = "internal")]
101 repository_map: StateRegistry::new(),
102 }),
103 }
104 }
105}
106
107fn new_http_client_builder() -> reqwest::ClientBuilder {
108 #[allow(unused_mut)]
109 let mut client_builder = reqwest::Client::builder();
110
111 #[cfg(not(target_arch = "wasm32"))]
112 {
113 use rustls::ClientConfig;
114 use rustls_platform_verifier::ConfigVerifierExt;
115 client_builder = client_builder.use_preconfigured_tls(
116 ClientConfig::with_platform_verifier().expect("Failed to create platform verifier"),
117 );
118
119 #[cfg(not(debug_assertions))]
121 {
122 client_builder = client_builder.https_only(true);
123 }
124 }
125
126 client_builder
127}
128
129fn build_default_headers(settings: &ClientSettings) -> header::HeaderMap {
131 let mut headers = header::HeaderMap::new();
132
133 if let Some(device_identifier) = &settings.device_identifier {
136 headers.append(
137 "Device-Identifier",
138 HeaderValue::from_str(device_identifier)
139 .expect("Device identifier should be a valid header value"),
140 );
141 }
142
143 if let Some(client_type) = Into::<Option<ClientName>>::into(settings.device_type) {
144 headers.append(
145 "Bitwarden-Client-Name",
146 HeaderValue::from_str(&client_type.to_string())
147 .expect("All ASCII strings are valid header values"),
148 );
149 }
150
151 if let Some(version) = &settings.bitwarden_client_version {
152 headers.append(
153 "Bitwarden-Client-Version",
154 HeaderValue::from_str(version).expect("Version should be a valid header value"),
155 );
156 }
157
158 if let Some(package_type) = &settings.bitwarden_package_type {
159 headers.append(
160 "Bitwarden-Package-Type",
161 HeaderValue::from_str(package_type)
162 .expect("Package type should be a valid header value"),
163 );
164 }
165
166 headers.append(
169 "Device-Type",
170 HeaderValue::from_str(&(settings.device_type as u8).to_string())
171 .expect("All numbers are valid ASCII"),
172 );
173
174 headers.append(
177 reqwest::header::USER_AGENT,
178 HeaderValue::from_str(&settings.user_agent)
179 .expect("User agent should be a valid header value"),
180 );
181
182 headers
183}