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, ClientManagedTokens, SdkManagedTokens, 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    /// Create a new Bitwarden client with SDK-managed tokens.
29    pub fn new(settings: Option<ClientSettings>) -> Self {
30        Self::new_internal(settings, Tokens::SdkManaged(SdkManagedTokens::default()))
31    }
32
33    /// Create a new Bitwarden client with client-managed tokens.
34    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        fn new_client_builder() -> reqwest::ClientBuilder {
45            #[allow(unused_mut)]
46            let mut client_builder = reqwest::Client::builder();
47
48            #[cfg(not(target_arch = "wasm32"))]
49            {
50                use rustls::ClientConfig;
51                use rustls_platform_verifier::ConfigVerifierExt;
52                client_builder = client_builder.use_preconfigured_tls(
53                    ClientConfig::with_platform_verifier()
54                        .expect("Failed to create platform verifier"),
55                );
56
57                // Enforce HTTPS for all requests in non-debug builds
58                #[cfg(not(debug_assertions))]
59                {
60                    client_builder = client_builder.https_only(true);
61                }
62            }
63
64            client_builder
65        }
66
67        let external_client = new_client_builder().build().expect("Build should not fail");
68
69        let mut headers = header::HeaderMap::new();
70        headers.append(
71            "Device-Type",
72            HeaderValue::from_str(&(settings.device_type as u8).to_string())
73                .expect("All numbers are valid ASCII"),
74        );
75        let client_builder = new_client_builder().default_headers(headers);
76
77        let client = client_builder.build().expect("Build should not fail");
78
79        let identity = bitwarden_api_identity::apis::configuration::Configuration {
80            base_path: settings.identity_url,
81            user_agent: Some(settings.user_agent.clone()),
82            client: client.clone(),
83            basic_auth: None,
84            oauth_access_token: None,
85            bearer_access_token: None,
86            api_key: None,
87        };
88
89        let api = bitwarden_api_api::apis::configuration::Configuration {
90            base_path: settings.api_url,
91            user_agent: Some(settings.user_agent),
92            client,
93            basic_auth: None,
94            oauth_access_token: None,
95            bearer_access_token: None,
96            api_key: None,
97        };
98
99        Self {
100            internal: Arc::new(InternalClient {
101                user_id: OnceLock::new(),
102                tokens: RwLock::new(tokens),
103                login_method: RwLock::new(None),
104                #[cfg(feature = "internal")]
105                flags: RwLock::new(Flags::default()),
106                __api_configurations: RwLock::new(Arc::new(ApiConfigurations {
107                    identity,
108                    api,
109                    device_type: settings.device_type,
110                })),
111                external_client,
112                key_store: KeyStore::default(),
113                #[cfg(feature = "internal")]
114                security_state: RwLock::new(None),
115                #[cfg(feature = "internal")]
116                repository_map: StateRegistry::new(),
117            }),
118        }
119    }
120}