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 client: bw_http_client.clone().into(),
68 };
69
70 let auth_middleware = token_handler.initialize_middleware(
72 login_method.clone(),
73 identity.clone(),
74 key_store.clone(),
75 );
76 let bw_http_client = reqwest_middleware::ClientBuilder::new(bw_http_client)
77 .with_arc(auth_middleware)
78 .build();
79 let api = bitwarden_api_api::Configuration {
80 base_path: settings.api_url,
81 client: bw_http_client,
82 };
83
84 Self {
85 internal: Arc::new(InternalClient {
86 user_id: OnceLock::new(),
87 token_handler,
88 login_method,
89 #[cfg(feature = "internal")]
90 flags: RwLock::new(Flags::default()),
91 api_configurations: ApiConfigurations::new(identity, api, settings.device_type),
92 external_http_client,
93 key_store,
94 #[cfg(feature = "internal")]
95 security_state: RwLock::new(None),
96 #[cfg(feature = "internal")]
97 repository_map: StateRegistry::new(),
98 }),
99 }
100 }
101}
102
103fn new_http_client_builder() -> reqwest::ClientBuilder {
104 #[allow(unused_mut)]
105 let mut client_builder = reqwest::Client::builder();
106
107 #[cfg(not(target_arch = "wasm32"))]
108 {
109 use rustls::ClientConfig;
110 use rustls_platform_verifier::ConfigVerifierExt;
111 client_builder = client_builder.use_preconfigured_tls(
112 ClientConfig::with_platform_verifier().expect("Failed to create platform verifier"),
113 );
114
115 #[cfg(not(debug_assertions))]
117 {
118 client_builder = client_builder.https_only(true);
119 }
120 }
121
122 client_builder
123}
124
125fn build_default_headers(settings: &ClientSettings) -> header::HeaderMap {
127 let mut headers = header::HeaderMap::new();
128
129 if let Some(device_identifier) = &settings.device_identifier {
132 headers.append(
133 "Device-Identifier",
134 HeaderValue::from_str(device_identifier)
135 .expect("Device identifier should be a valid header value"),
136 );
137 }
138
139 if let Some(client_type) = Into::<Option<ClientName>>::into(settings.device_type) {
140 headers.append(
141 "Bitwarden-Client-Name",
142 HeaderValue::from_str(&client_type.to_string())
143 .expect("All ASCII strings are valid header values"),
144 );
145 }
146
147 if let Some(version) = &settings.bitwarden_client_version {
148 headers.append(
149 "Bitwarden-Client-Version",
150 HeaderValue::from_str(version).expect("Version should be a valid header value"),
151 );
152 }
153
154 if let Some(package_type) = &settings.bitwarden_package_type {
155 headers.append(
156 "Bitwarden-Package-Type",
157 HeaderValue::from_str(package_type)
158 .expect("Package type should be a valid header value"),
159 );
160 }
161
162 headers.append(
165 "Device-Type",
166 HeaderValue::from_str(&(settings.device_type as u8).to_string())
167 .expect("All numbers are valid ASCII"),
168 );
169
170 headers.append(
171 reqwest::header::USER_AGENT,
172 HeaderValue::from_str(&settings.user_agent)
173 .expect("User agent should be a valid header value"),
174 );
175
176 headers
177}