diff --git a/crates/bitwarden/src/auth/login/api_key.rs b/crates/bitwarden/src/auth/login/api_key.rs index 046bae0ca..795a3c7b4 100644 --- a/crates/bitwarden/src/auth/login/api_key.rs +++ b/crates/bitwarden/src/auth/login/api_key.rs @@ -3,13 +3,11 @@ use std::str::FromStr; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use super::request_prelogin; use crate::{ auth::{ api::{request::ApiTokenRequest, response::IdentityTokenResponse}, - login::{ - determine_password_hash, response::two_factor::TwoFactorProviders, - PasswordLoginResponse, - }, + login::{response::two_factor::TwoFactorProviders, PasswordLoginResponse}, }, client::{LoginMethod, UserLoginMethod}, crypto::EncString, @@ -18,8 +16,6 @@ use crate::{ Client, }; -use super::request_prelogin; - pub(crate) async fn api_key_login( client: &mut Client, input: &ApiKeyLoginRequest, @@ -30,6 +26,15 @@ pub(crate) async fn api_key_login( let response = request_api_identity_tokens(client, input).await?; if let IdentityTokenResponse::Authenticated(r) = &response { + let access_token_obj = decode_token(&r.access_token)?; + + // This should always be Some() when logging in with an api key + let email = access_token_obj + .email + .ok_or(Error::Internal("Access token doesn't contain email"))?; + + let kdf = request_prelogin(client, email.clone()).await?.try_into()?; + client.set_tokens( r.access_token.clone(), r.refresh_token.clone(), @@ -37,19 +42,11 @@ pub(crate) async fn api_key_login( LoginMethod::User(UserLoginMethod::ApiKey { client_id: input.client_id.to_owned(), client_secret: input.client_secret.to_owned(), + email, + kdf, }), ); - let access_token_obj = decode_token(&r.access_token)?; - - // This should always be Some() when logging in with an api key - let email = access_token_obj - .email - .ok_or(Error::Internal("Access token doesn't contain email"))?; - - let kdf = request_prelogin(client, email.clone()).await?.try_into()?; - let _ = determine_password_hash(&email, &kdf, &input.password).await?; - let user_key = EncString::from_str(r.key.as_deref().unwrap()).unwrap(); let private_key = EncString::from_str(r.private_key.as_deref().unwrap()).unwrap(); diff --git a/crates/bitwarden/src/auth/login/mod.rs b/crates/bitwarden/src/auth/login/mod.rs index 2b2892d07..9e1dbb818 100644 --- a/crates/bitwarden/src/auth/login/mod.rs +++ b/crates/bitwarden/src/auth/login/mod.rs @@ -40,19 +40,18 @@ pub(crate) use access_token::access_token_login; pub use access_token::{AccessTokenLoginRequest, AccessTokenLoginResponse}; #[cfg(feature = "internal")] -async fn determine_password_hash( - email: &str, - kdf: &Kdf, - password: &str, -) -> Result { - use crate::crypto::{MasterKey, HashPurpose}; - +async fn determine_password_hash(email: &str, kdf: &Kdf, password: &str) -> Result { + use crate::crypto::{HashPurpose, MasterKey}; + let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?; master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization) } #[cfg(feature = "internal")] -pub(crate) async fn request_prelogin(client: &mut Client, email: String) -> Result { +pub(crate) async fn request_prelogin( + client: &mut Client, + email: String, +) -> Result { let request_model = PreloginRequestModel::new(email); let config = client.get_api_configurations().await; Ok(accounts_prelogin_post(&config.identity, Some(request_model)).await?) diff --git a/crates/bitwarden/src/auth/login/password.rs b/crates/bitwarden/src/auth/login/password.rs index c6eb765f4..f596843fa 100644 --- a/crates/bitwarden/src/auth/login/password.rs +++ b/crates/bitwarden/src/auth/login/password.rs @@ -21,7 +21,8 @@ use crate::{ api::response::IdentityTokenResponse, login::response::{captcha_response::CaptchaResponse, two_factor::TwoFactorProviders}, }, - error::Result, client::kdf::Kdf, + client::kdf::Kdf, + error::Result, }; #[cfg(feature = "internal")] @@ -29,13 +30,12 @@ pub(crate) async fn password_login( client: &mut Client, input: &PasswordLoginRequest, ) -> Result { - use crate::{client::UserLoginMethod, auth::login::request_prelogin}; + use crate::client::UserLoginMethod; info!("password logging in"); debug!("{:#?}, {:#?}", client, input); - let kdf = request_prelogin(client, input.email.clone()).await?.try_into()?; - let password_hash = determine_password_hash(&input.email, &kdf, &input.password).await?; + let password_hash = determine_password_hash(&input.email, &input.kdf, &input.password).await?; let response = request_identity_tokens(client, input, &password_hash).await?; if let IdentityTokenResponse::Authenticated(r) = &response { @@ -43,7 +43,7 @@ pub(crate) async fn password_login( r.access_token.clone(), r.refresh_token.clone(), r.expires_in, - LoginMethod::User(UserLoginMethod::Username { + LoginMethod::User(UserLoginMethod::Username { client_id: "web".to_owned(), email: input.email.to_owned(), kdf: input.kdf.to_owned(), diff --git a/crates/bitwarden/src/auth/login/two_factor.rs b/crates/bitwarden/src/auth/login/two_factor.rs index aae8f7243..9ede518cc 100644 --- a/crates/bitwarden/src/auth/login/two_factor.rs +++ b/crates/bitwarden/src/auth/login/two_factor.rs @@ -19,7 +19,9 @@ pub(crate) async fn send_two_factor_email( client: &mut Client, input: &TwoFactorEmailRequest, ) -> Result<()> { - let kdf = request_prelogin(client, input.email.clone()).await?.try_into()?; + let kdf = request_prelogin(client, input.email.clone()) + .await? + .try_into()?; let password_hash = determine_password_hash(&input.email, &kdf, &input.password).await?; let config = client.get_api_configurations().await; diff --git a/crates/bitwarden/src/auth/renew.rs b/crates/bitwarden/src/auth/renew.rs index 8f8226c92..fe5213795 100644 --- a/crates/bitwarden/src/auth/renew.rs +++ b/crates/bitwarden/src/auth/renew.rs @@ -35,6 +35,7 @@ pub(crate) async fn renew_token(client: &mut Client) -> Result<()> { UserLoginMethod::ApiKey { client_id, client_secret, + .. } => { ApiTokenRequest::new(client_id, client_secret) .send(&client.__api_configurations) diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 4ebe46d60..8c57aaa31 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -26,13 +26,12 @@ use crate::{ client::{ client_settings::{ClientSettings, DeviceType}, encryption_settings::EncryptionSettings, + kdf::Kdf, }, crypto::SymmetricCryptoKey, error::{Error, Result}, }; -use super::kdf::Kdf; - #[derive(Debug)] pub(crate) struct ApiConfigurations { pub identity: bitwarden_api_identity::apis::configuration::Configuration, @@ -59,6 +58,9 @@ pub(crate) enum UserLoginMethod { ApiKey { client_id: String, client_secret: String, + + email: String, + kdf: Kdf, }, } @@ -139,10 +141,7 @@ impl Client { } #[cfg(feature = "internal")] - pub async fn prelogin( - &mut self, - email: String, - ) -> Result { + pub async fn prelogin(&mut self, email: String) -> Result { use crate::auth::login::request_prelogin; request_prelogin(self, email).await?.try_into() @@ -193,9 +192,9 @@ impl Client { pub fn get_access_token_organization(&self) -> Option { match self.login_method { Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken { - organization_id, - .. - })) => return Some(organization_id), + organization_id, + .. + })) => return Some(organization_id), _ => None, } } diff --git a/crates/bitwarden/src/client/encryption_settings.rs b/crates/bitwarden/src/client/encryption_settings.rs index 045deddfa..ded01f35f 100644 --- a/crates/bitwarden/src/client/encryption_settings.rs +++ b/crates/bitwarden/src/client/encryption_settings.rs @@ -36,7 +36,8 @@ impl EncryptionSettings { use crate::{client::UserLoginMethod, crypto::MasterKey}; match login_method { - LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) => { + LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) + | LoginMethod::User(UserLoginMethod::ApiKey { email, kdf, .. }) => { // Derive master key from password let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?; @@ -58,7 +59,6 @@ impl EncryptionSettings { org_keys: HashMap::new(), }) } - LoginMethod::User(UserLoginMethod::ApiKey { .. }) => todo!(), LoginMethod::ServiceAccount(_) => todo!(), } } diff --git a/crates/bitwarden/src/client/mod.rs b/crates/bitwarden/src/client/mod.rs index 7197ba2de..25a2f5db0 100644 --- a/crates/bitwarden/src/client/mod.rs +++ b/crates/bitwarden/src/client/mod.rs @@ -2,11 +2,11 @@ pub(crate) use client::*; pub(crate) mod access_token; -pub mod kdf; #[allow(clippy::module_inception)] mod client; pub mod client_settings; pub(crate) mod encryption_settings; +pub mod kdf; pub use access_token::AccessToken; pub use client::Client; diff --git a/crates/bitwarden/src/vault/send.rs b/crates/bitwarden/src/vault/send.rs index ab7eed024..714feb613 100644 --- a/crates/bitwarden/src/vault/send.rs +++ b/crates/bitwarden/src/vault/send.rs @@ -280,7 +280,9 @@ impl Encryptable for SendView { #[cfg(test)] mod tests { use super::Send; - use crate::client::{encryption_settings::EncryptionSettings, kdf::Kdf, LoginMethod, UserLoginMethod}; + use crate::client::{ + encryption_settings::EncryptionSettings, kdf::Kdf, LoginMethod, UserLoginMethod, + }; #[test] fn test_get_send_key() { diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index bb1621184..29252d133 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -24,6 +24,7 @@ inquire = "0.6.2" bitwarden = { path = "../bitwarden", version = "0.3.0", features = [ "internal", + "mobile", ] } bitwarden-cli = { path = "../bitwarden-cli", version = "0.1.0" } diff --git a/crates/bw/src/auth/login.rs b/crates/bw/src/auth/login.rs index 322a1491e..1c169817f 100644 --- a/crates/bw/src/auth/login.rs +++ b/crates/bw/src/auth/login.rs @@ -68,7 +68,7 @@ pub(crate) async fn password_login(mut client: Client, email: Option) -> email, password, two_factor, - kdf + kdf, }) .await?;