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    /// Load feature flags. This is intentionally a collection and not the internal `Flag` enum as
114    /// we want to avoid changes in feature flags from being a breaking change.
115    #[cfg(feature = "internal")]
116    pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
117        *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
118    }
119
120    /// Retrieve the active feature flags.
121    #[cfg(feature = "internal")]
122    pub fn get_flags(&self) -> Flags {
123        self.flags.read().expect("RwLock is not poisoned").clone()
124    }
125
126    #[cfg(feature = "internal")]
127    pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
128        self.login_method
129            .read()
130            .expect("RwLock is not poisoned")
131            .clone()
132    }
133
134    #[allow(missing_docs)]
135    pub fn get_access_token_organization(&self) -> Option<Uuid> {
136        match self
137            .login_method
138            .read()
139            .expect("RwLock is not poisoned")
140            .as_deref()
141        {
142            #[cfg(feature = "secrets")]
143            Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
144                organization_id,
145                ..
146            })) => Some(*organization_id),
147            _ => None,
148        }
149    }
150
151    #[cfg(any(feature = "internal", feature = "secrets"))]
152    pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
153        use log::debug;
154
155        debug! {"setting login method: {login_method:#?}"}
156        *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
157    }
158
159    pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
160        *self.tokens.write().expect("RwLock is not poisoned") =
161            Tokens::SdkManaged(SdkManagedTokens {
162                access_token: Some(token.clone()),
163                expires_on: Some(Utc::now().timestamp() + expires_in as i64),
164                refresh_token,
165            });
166        self.set_api_tokens_internal(token);
167    }
168
169    /// Sets api tokens for only internal API clients, use `set_tokens` for SdkManagedTokens.
170    pub(crate) fn set_api_tokens_internal(&self, token: String) {
171        let mut guard = self
172            .__api_configurations
173            .write()
174            .expect("RwLock is not poisoned");
175
176        let inner = Arc::make_mut(&mut guard);
177        inner.identity.oauth_access_token = Some(token.clone());
178        inner.api.oauth_access_token = Some(token);
179    }
180
181    #[allow(missing_docs)]
182    #[cfg(feature = "internal")]
183    pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
184        match self
185            .login_method
186            .read()
187            .expect("RwLock is not poisoned")
188            .as_deref()
189        {
190            Some(LoginMethod::User(
191                UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
192            )) => Ok(kdf.clone()),
193            _ => Err(NotAuthenticatedError),
194        }
195    }
196
197    #[allow(missing_docs)]
198    pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
199        // At the moment we ignore the error result from the token renewal, if it fails,
200        // the token will end up expiring and the next operation is going to fail anyway.
201        renew_token(self).await.ok();
202        self.__api_configurations
203            .read()
204            .expect("RwLock is not poisoned")
205            .clone()
206    }
207
208    #[allow(missing_docs)]
209    #[cfg(feature = "internal")]
210    pub fn get_http_client(&self) -> &reqwest::Client {
211        &self.external_client
212    }
213
214    #[allow(missing_docs)]
215    pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
216        &self.key_store
217    }
218
219    /// Returns the security version of the user.
220    /// `1` is returned for V1 users that do not have a signed security state.
221    /// `2` or greater is returned for V2 users that have a signed security state.
222    #[cfg(feature = "internal")]
223    pub fn get_security_version(&self) -> u64 {
224        self.security_state
225            .read()
226            .expect("RwLock is not poisoned")
227            .as_ref()
228            .map_or(1, |state| state.version())
229    }
230
231    #[allow(missing_docs)]
232    pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
233        let set_uuid = self.user_id.get_or_init(|| user_id);
234
235        // Only return an error if the user_id is already set to a different value,
236        // as we want an SDK client to be tied to a single user_id.
237        // If it's the same value, we can just do nothing.
238        if *set_uuid != user_id {
239            Err(UserIdAlreadySetError)
240        } else {
241            Ok(())
242        }
243    }
244
245    #[allow(missing_docs)]
246    pub fn get_user_id(&self) -> Option<Uuid> {
247        self.user_id.get().copied()
248    }
249
250    #[cfg(feature = "internal")]
251    pub(crate) fn initialize_user_crypto_master_key(
252        &self,
253        master_key: MasterKey,
254        user_key: EncString,
255        key_state: UserKeyState,
256    ) -> Result<(), EncryptionSettingsError> {
257        let user_key = master_key.decrypt_user_key(user_key)?;
258        self.initialize_user_crypto_decrypted_key(user_key, key_state)
259    }
260
261    #[cfg(feature = "internal")]
262    pub(crate) fn initialize_user_crypto_decrypted_key(
263        &self,
264        user_key: SymmetricCryptoKey,
265        key_state: UserKeyState,
266    ) -> Result<(), EncryptionSettingsError> {
267        match user_key {
268            SymmetricCryptoKey::Aes256CbcHmacKey(ref user_key) => {
269                EncryptionSettings::new_decrypted_key(
270                    AccountEncryptionKeys::V1 {
271                        user_key: user_key.clone(),
272                        private_key: key_state.private_key,
273                    },
274                    &self.key_store,
275                    &self.security_state,
276                )?;
277            }
278            SymmetricCryptoKey::XChaCha20Poly1305Key(ref user_key) => {
279                EncryptionSettings::new_decrypted_key(
280                    AccountEncryptionKeys::V2 {
281                        user_key: user_key.clone(),
282                        private_key: key_state.private_key,
283                        signing_key: key_state
284                            .signing_key
285                            .ok_or(EncryptionSettingsError::InvalidSigningKey)?,
286                        security_state: key_state
287                            .security_state
288                            .ok_or(EncryptionSettingsError::InvalidSecurityState)?,
289                    },
290                    &self.key_store,
291                    &self.security_state,
292                )?;
293            }
294            _ => {
295                return Err(CryptoError::InvalidKey.into());
296            }
297        }
298
299        Ok(())
300    }
301
302    #[cfg(feature = "internal")]
303    pub(crate) fn initialize_user_crypto_pin(
304        &self,
305        pin_key: PinKey,
306        pin_protected_user_key: EncString,
307        key_state: UserKeyState,
308    ) -> Result<(), EncryptionSettingsError> {
309        let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
310        self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
311    }
312
313    #[cfg(feature = "secrets")]
314    pub(crate) fn initialize_crypto_single_org_key(
315        &self,
316        organization_id: Uuid,
317        key: SymmetricCryptoKey,
318    ) {
319        EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
320    }
321
322    #[allow(missing_docs)]
323    #[cfg(feature = "internal")]
324    pub fn initialize_org_crypto(
325        &self,
326        org_keys: Vec<(Uuid, UnsignedSharedKey)>,
327    ) -> Result<(), EncryptionSettingsError> {
328        EncryptionSettings::set_org_keys(org_keys, &self.key_store)
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    use crate::Client;
335
336    #[test]
337    fn initializing_user_multiple_times() {
338        use super::*;
339
340        let client = Client::new(None);
341        let user_id = Uuid::new_v4();
342
343        // Setting the user ID for the first time should work.
344        assert!(client.internal.init_user_id(user_id).is_ok());
345        assert_eq!(client.internal.get_user_id(), Some(user_id));
346
347        // Trying to set the same user_id again should not return an error.
348        assert!(client.internal.init_user_id(user_id).is_ok());
349
350        // Trying to set a different user_id should return an error.
351        let different_user_id = Uuid::new_v4();
352        assert!(client.internal.init_user_id(different_user_id).is_err());
353    }
354}