bitwarden_core/client/
client.rs

1use 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::ClientSettings,
13    internal::{ApiConfigurations, Tokens},
14};
15
16/// The main struct to interact with the Bitwarden SDK.
17#[derive(Debug, Clone)]
18pub struct Client {
19    // Important: The [`Client`] struct requires its `Clone` implementation to return an owned
20    // reference to the same instance. This is required to properly use the FFI API, where we can't
21    // just use normal Rust references effectively. For this to happen, any mutable state needs
22    // to be behind an Arc, ideally as part of the existing [`InternalClient`] struct.
23    #[doc(hidden)]
24    pub internal: Arc<InternalClient>,
25}
26
27impl Client {
28    #[allow(missing_docs)]
29    pub fn new(settings_input: Option<ClientSettings>) -> Self {
30        let settings = settings_input.unwrap_or_default();
31
32        fn new_client_builder() -> reqwest::ClientBuilder {
33            #[allow(unused_mut)]
34            let mut client_builder = reqwest::Client::builder();
35
36            #[cfg(not(target_arch = "wasm32"))]
37            {
38                use rustls::ClientConfig;
39                use rustls_platform_verifier::ConfigVerifierExt;
40                client_builder = client_builder.use_preconfigured_tls(
41                    ClientConfig::with_platform_verifier()
42                        .expect("Failed to create platform verifier"),
43                );
44            }
45
46            client_builder
47        }
48
49        let external_client = new_client_builder().build().expect("Build should not fail");
50
51        let mut headers = header::HeaderMap::new();
52        headers.append(
53            "Device-Type",
54            HeaderValue::from_str(&(settings.device_type as u8).to_string())
55                .expect("All numbers are valid ASCII"),
56        );
57        let client_builder = new_client_builder().default_headers(headers);
58
59        let client = client_builder.build().expect("Build should not fail");
60
61        let identity = bitwarden_api_identity::apis::configuration::Configuration {
62            base_path: settings.identity_url,
63            user_agent: Some(settings.user_agent.clone()),
64            client: client.clone(),
65            basic_auth: None,
66            oauth_access_token: None,
67            bearer_access_token: None,
68            api_key: None,
69        };
70
71        let api = bitwarden_api_api::apis::configuration::Configuration {
72            base_path: settings.api_url,
73            user_agent: Some(settings.user_agent),
74            client,
75            basic_auth: None,
76            oauth_access_token: None,
77            bearer_access_token: None,
78            api_key: None,
79        };
80
81        Self {
82            internal: Arc::new(InternalClient {
83                user_id: OnceLock::new(),
84                tokens: RwLock::new(Tokens::default()),
85                login_method: RwLock::new(None),
86                #[cfg(feature = "internal")]
87                flags: RwLock::new(Flags::default()),
88                __api_configurations: RwLock::new(Arc::new(ApiConfigurations {
89                    identity,
90                    api,
91                    device_type: settings.device_type,
92                })),
93                external_client,
94                key_store: KeyStore::default(),
95                #[cfg(feature = "internal")]
96                repository_map: StateRegistry::new(),
97            }),
98        }
99    }
100}