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
58            client_builder
59        }
60
61        let external_client = new_client_builder().build().expect("Build should not fail");
62
63        let mut headers = header::HeaderMap::new();
64        headers.append(
65            "Device-Type",
66            HeaderValue::from_str(&(settings.device_type as u8).to_string())
67                .expect("All numbers are valid ASCII"),
68        );
69        let client_builder = new_client_builder().default_headers(headers);
70
71        let client = client_builder.build().expect("Build should not fail");
72
73        let identity = bitwarden_api_identity::apis::configuration::Configuration {
74            base_path: settings.identity_url,
75            user_agent: Some(settings.user_agent.clone()),
76            client: client.clone(),
77            basic_auth: None,
78            oauth_access_token: None,
79            bearer_access_token: None,
80            api_key: None,
81        };
82
83        let api = bitwarden_api_api::apis::configuration::Configuration {
84            base_path: settings.api_url,
85            user_agent: Some(settings.user_agent),
86            client,
87            basic_auth: None,
88            oauth_access_token: None,
89            bearer_access_token: None,
90            api_key: None,
91        };
92
93        Self {
94            internal: Arc::new(InternalClient {
95                user_id: OnceLock::new(),
96                tokens: RwLock::new(tokens),
97                login_method: RwLock::new(None),
98                #[cfg(feature = "internal")]
99                flags: RwLock::new(Flags::default()),
100                __api_configurations: RwLock::new(Arc::new(ApiConfigurations {
101                    identity,
102                    api,
103                    device_type: settings.device_type,
104                })),
105                external_client,
106                key_store: KeyStore::default(),
107                #[cfg(feature = "internal")]
108                security_state: RwLock::new(None),
109                #[cfg(feature = "internal")]
110                repository_map: StateRegistry::new(),
111            }),
112        }
113    }
114}