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