bitwarden_core/client/
internal.rs

1use std::sync::{Arc, OnceLock, RwLock};
2
3use bitwarden_crypto::KeyStore;
4#[cfg(any(feature = "internal", feature = "secrets"))]
5use bitwarden_crypto::SymmetricCryptoKey;
6#[cfg(feature = "internal")]
7use bitwarden_crypto::{EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey};
8#[cfg(feature = "internal")]
9use bitwarden_state::registry::StateRegistry;
10use chrono::Utc;
11use uuid::Uuid;
12
13#[cfg(any(feature = "internal", feature = "secrets"))]
14use crate::client::encryption_settings::EncryptionSettings;
15#[cfg(feature = "secrets")]
16use crate::client::login_method::ServiceAccountLoginMethod;
17use crate::{
18    auth::renew::renew_token, client::login_method::LoginMethod, error::UserIdAlreadySetError,
19    key_management::KeyIds, DeviceType,
20};
21#[cfg(feature = "internal")]
22use crate::{
23    client::encryption_settings::EncryptionSettingsError,
24    client::{flags::Flags, login_method::UserLoginMethod},
25    error::NotAuthenticatedError,
26};
27
28#[allow(missing_docs)]
29#[derive(Debug, Clone)]
30pub struct ApiConfigurations {
31    pub identity: bitwarden_api_identity::apis::configuration::Configuration,
32    pub api: bitwarden_api_api::apis::configuration::Configuration,
33    pub device_type: DeviceType,
34}
35
36/// Access and refresh tokens used for authentication and authorization.
37#[derive(Debug, Clone)]
38pub(crate) enum Tokens {
39    SdkManaged(SdkManagedTokens),
40    ClientManaged(Arc<dyn ClientManagedTokens>),
41}
42
43/// Access tokens managed by client applications, such as the web or mobile apps.
44#[async_trait::async_trait]
45pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
46    /// Returns the access token, if available.
47    async fn get_access_token(&self) -> Option<String>;
48}
49
50/// Tokens managed by the SDK, the SDK will automatically handle token renewal.
51#[derive(Debug, Default, Clone)]
52pub(crate) struct SdkManagedTokens {
53    // These two fields are always written to, but they are not read
54    // from the secrets manager SDK.
55    #[allow(dead_code)]
56    access_token: Option<String>,
57    pub(crate) expires_on: Option<i64>,
58
59    #[cfg_attr(not(feature = "internal"), allow(dead_code))]
60    pub(crate) refresh_token: Option<String>,
61}
62
63#[allow(missing_docs)]
64#[derive(Debug)]
65pub struct InternalClient {
66    pub(crate) user_id: OnceLock<Uuid>,
67    pub(crate) tokens: RwLock<Tokens>,
68    pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
69
70    #[cfg(feature = "internal")]
71    pub(super) flags: RwLock<Flags>,
72
73    /// Use Client::get_api_configurations().await to access this.
74    /// It should only be used directly in renew_token
75    #[doc(hidden)]
76    pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
77
78    /// Reqwest client useable for external integrations like email forwarders, HIBP.
79    #[allow(unused)]
80    pub(crate) external_client: reqwest::Client,
81
82    pub(super) key_store: KeyStore<KeyIds>,
83
84    #[cfg(feature = "internal")]
85    pub(crate) repository_map: StateRegistry,
86}
87
88impl InternalClient {
89    #[allow(missing_docs)]
90    #[cfg(feature = "internal")]
91    pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
92        *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
93    }
94
95    #[allow(missing_docs)]
96    #[cfg(feature = "internal")]
97    pub fn get_flags(&self) -> Flags {
98        self.flags.read().expect("RwLock is not poisoned").clone()
99    }
100
101    #[cfg(feature = "internal")]
102    pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
103        self.login_method
104            .read()
105            .expect("RwLock is not poisoned")
106            .clone()
107    }
108
109    #[allow(missing_docs)]
110    pub fn get_access_token_organization(&self) -> Option<Uuid> {
111        match self
112            .login_method
113            .read()
114            .expect("RwLock is not poisoned")
115            .as_deref()
116        {
117            #[cfg(feature = "secrets")]
118            Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
119                organization_id,
120                ..
121            })) => Some(*organization_id),
122            _ => None,
123        }
124    }
125
126    #[cfg(any(feature = "internal", feature = "secrets"))]
127    pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
128        use log::debug;
129
130        debug! {"setting login method: {login_method:#?}"}
131        *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
132    }
133
134    pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
135        *self.tokens.write().expect("RwLock is not poisoned") =
136            Tokens::SdkManaged(SdkManagedTokens {
137                access_token: Some(token.clone()),
138                expires_on: Some(Utc::now().timestamp() + expires_in as i64),
139                refresh_token,
140            });
141        self.set_api_tokens_internal(token);
142    }
143
144    /// Sets api tokens for only internal API clients, use `set_tokens` for SdkManagedTokens.
145    pub(crate) fn set_api_tokens_internal(&self, token: String) {
146        let mut guard = self
147            .__api_configurations
148            .write()
149            .expect("RwLock is not poisoned");
150
151        let inner = Arc::make_mut(&mut guard);
152        inner.identity.oauth_access_token = Some(token.clone());
153        inner.api.oauth_access_token = Some(token);
154    }
155
156    #[allow(missing_docs)]
157    #[cfg(feature = "internal")]
158    pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
159        match self
160            .login_method
161            .read()
162            .expect("RwLock is not poisoned")
163            .as_deref()
164        {
165            Some(LoginMethod::User(
166                UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
167            )) => Ok(kdf.clone()),
168            _ => Err(NotAuthenticatedError),
169        }
170    }
171
172    #[allow(missing_docs)]
173    pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
174        // At the moment we ignore the error result from the token renewal, if it fails,
175        // the token will end up expiring and the next operation is going to fail anyway.
176        renew_token(self).await.ok();
177        self.__api_configurations
178            .read()
179            .expect("RwLock is not poisoned")
180            .clone()
181    }
182
183    #[allow(missing_docs)]
184    #[cfg(feature = "internal")]
185    pub fn get_http_client(&self) -> &reqwest::Client {
186        &self.external_client
187    }
188
189    #[allow(missing_docs)]
190    pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
191        &self.key_store
192    }
193
194    #[allow(missing_docs)]
195    pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
196        let set_uuid = self.user_id.get_or_init(|| user_id);
197
198        // Only return an error if the user_id is already set to a different value,
199        // as we want an SDK client to be tied to a single user_id.
200        // If it's the same value, we can just do nothing.
201        if *set_uuid != user_id {
202            Err(UserIdAlreadySetError)
203        } else {
204            Ok(())
205        }
206    }
207
208    #[allow(missing_docs)]
209    pub fn get_user_id(&self) -> Option<Uuid> {
210        self.user_id.get().copied()
211    }
212
213    #[cfg(feature = "internal")]
214    pub(crate) fn initialize_user_crypto_master_key(
215        &self,
216        master_key: MasterKey,
217        user_key: EncString,
218        private_key: EncString,
219        signing_key: Option<EncString>,
220    ) -> Result<(), EncryptionSettingsError> {
221        let user_key = master_key.decrypt_user_key(user_key)?;
222        EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
223
224        Ok(())
225    }
226
227    #[cfg(feature = "internal")]
228    pub(crate) fn initialize_user_crypto_decrypted_key(
229        &self,
230        user_key: SymmetricCryptoKey,
231        private_key: EncString,
232        signing_key: Option<EncString>,
233    ) -> Result<(), EncryptionSettingsError> {
234        EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
235
236        Ok(())
237    }
238
239    #[cfg(feature = "internal")]
240    pub(crate) fn initialize_user_crypto_pin(
241        &self,
242        pin_key: PinKey,
243        pin_protected_user_key: EncString,
244        private_key: EncString,
245        signing_key: Option<EncString>,
246    ) -> Result<(), EncryptionSettingsError> {
247        let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
248        self.initialize_user_crypto_decrypted_key(decrypted_user_key, private_key, signing_key)
249    }
250
251    #[cfg(feature = "secrets")]
252    pub(crate) fn initialize_crypto_single_org_key(
253        &self,
254        organization_id: Uuid,
255        key: SymmetricCryptoKey,
256    ) {
257        EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
258    }
259
260    #[allow(missing_docs)]
261    #[cfg(feature = "internal")]
262    pub fn initialize_org_crypto(
263        &self,
264        org_keys: Vec<(Uuid, UnsignedSharedKey)>,
265    ) -> Result<(), EncryptionSettingsError> {
266        EncryptionSettings::set_org_keys(org_keys, &self.key_store)
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use crate::Client;
273
274    #[test]
275    fn initializing_user_multiple_times() {
276        use super::*;
277
278        let client = Client::new(None);
279        let user_id = Uuid::new_v4();
280
281        // Setting the user ID for the first time should work.
282        assert!(client.internal.init_user_id(user_id).is_ok());
283        assert_eq!(client.internal.get_user_id(), Some(user_id));
284
285        // Trying to set the same user_id again should not return an error.
286        assert!(client.internal.init_user_id(user_id).is_ok());
287
288        // Trying to set a different user_id should return an error.
289        let different_user_id = Uuid::new_v4();
290        assert!(client.internal.init_user_id(different_user_id).is_err());
291    }
292}