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::client::{
12 client_settings::{ClientName, ClientSettings},
13 internal::{ApiConfigurations, ClientManagedTokens, SdkManagedTokens, Tokens},
14};
15
16#[derive(Debug, Clone)]
18pub struct Client {
19 #[doc(hidden)]
24 pub internal: Arc<InternalClient>,
25}
26
27impl Client {
28 pub fn new(settings: Option<ClientSettings>) -> Self {
30 Self::new_internal(settings, Tokens::SdkManaged(SdkManagedTokens::default()))
31 }
32
33 pub fn new_with_client_tokens(
35 settings: Option<ClientSettings>,
36 tokens: Arc<dyn ClientManagedTokens>,
37 ) -> Self {
38 Self::new_internal(settings, Tokens::ClientManaged(tokens))
39 }
40
41 fn new_internal(settings_input: Option<ClientSettings>, tokens: Tokens) -> Self {
42 let settings = settings_input.unwrap_or_default();
43
44 let external_http_client = new_http_client_builder()
45 .build()
46 .expect("External HTTP Client build should not fail");
47
48 let headers = build_default_headers(&settings);
49
50 let bw_http_client = new_http_client_builder()
51 .default_headers(headers)
52 .build()
53 .expect("Bw HTTP Client build should not fail");
54
55 let bw_http_client = reqwest_middleware::ClientBuilder::new(bw_http_client).build();
56
57 let identity = bitwarden_api_identity::apis::configuration::Configuration {
58 base_path: settings.identity_url,
59 user_agent: Some(settings.user_agent.clone()),
60 client: bw_http_client.clone(),
61 basic_auth: None,
62 oauth_access_token: None,
63 bearer_access_token: None,
64 api_key: None,
65 };
66
67 let api = bitwarden_api_api::apis::configuration::Configuration {
68 base_path: settings.api_url,
69 user_agent: Some(settings.user_agent),
70 client: bw_http_client,
71 basic_auth: None,
72 oauth_access_token: None,
73 bearer_access_token: None,
74 api_key: None,
75 };
76
77 Self {
78 internal: Arc::new(InternalClient {
79 user_id: OnceLock::new(),
80 tokens: RwLock::new(tokens),
81 login_method: RwLock::new(None),
82 #[cfg(feature = "internal")]
83 flags: RwLock::new(Flags::default()),
84 __api_configurations: RwLock::new(ApiConfigurations::new(
85 identity,
86 api,
87 settings.device_type,
88 )),
89 external_http_client,
90 key_store: KeyStore::default(),
91 #[cfg(feature = "internal")]
92 security_state: RwLock::new(None),
93 #[cfg(feature = "internal")]
94 repository_map: StateRegistry::new(),
95 }),
96 }
97 }
98}
99
100fn new_http_client_builder() -> reqwest::ClientBuilder {
101 #[allow(unused_mut)]
102 let mut client_builder = reqwest::Client::builder();
103
104 #[cfg(not(target_arch = "wasm32"))]
105 {
106 use rustls::ClientConfig;
107 use rustls_platform_verifier::ConfigVerifierExt;
108 client_builder = client_builder.use_preconfigured_tls(
109 ClientConfig::with_platform_verifier().expect("Failed to create platform verifier"),
110 );
111
112 #[cfg(not(debug_assertions))]
114 {
115 client_builder = client_builder.https_only(true);
116 }
117 }
118
119 client_builder
120}
121
122fn build_default_headers(settings: &ClientSettings) -> header::HeaderMap {
124 let mut headers = header::HeaderMap::new();
125
126 if let Some(device_identifier) = &settings.device_identifier {
129 headers.append(
130 "Device-Identifier",
131 HeaderValue::from_str(device_identifier)
132 .expect("Device identifier should be a valid header value"),
133 );
134 }
135
136 if let Some(client_type) = Into::<Option<ClientName>>::into(settings.device_type) {
137 headers.append(
138 "Bitwarden-Client-Name",
139 HeaderValue::from_str(&client_type.to_string())
140 .expect("All ASCII strings are valid header values"),
141 );
142 }
143
144 if let Some(version) = &settings.bitwarden_client_version {
145 headers.append(
146 "Bitwarden-Client-Version",
147 HeaderValue::from_str(version).expect("Version should be a valid header value"),
148 );
149 }
150
151 if let Some(package_type) = &settings.bitwarden_package_type {
152 headers.append(
153 "Bitwarden-Package-Type",
154 HeaderValue::from_str(package_type)
155 .expect("Package type should be a valid header value"),
156 );
157 }
158
159 headers.append(
162 "Device-Type",
163 HeaderValue::from_str(&(settings.device_type as u8).to_string())
164 .expect("All numbers are valid ASCII"),
165 );
166
167 headers.append(
170 reqwest::header::USER_AGENT,
171 HeaderValue::from_str(&settings.user_agent)
172 .expect("User agent should be a valid header value"),
173 );
174
175 headers
176}