bitwarden_core/auth/login/
password.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3#[cfg(feature = "internal")]
4use tracing::info;
5
6use crate::auth::{
7    api::response::IdentityTokenResponse, login::response::two_factor::TwoFactorProviders,
8};
9#[cfg(feature = "internal")]
10use crate::{
11    Client,
12    auth::{api::request::PasswordTokenRequest, login::LoginError, login::TwoFactorRequest},
13    client::LoginMethod,
14    key_management::{MasterPasswordAuthenticationData, UserDecryptionData},
15};
16
17#[cfg(feature = "internal")]
18pub(crate) async fn login_password(
19    client: &Client,
20    input: &PasswordLoginRequest,
21) -> Result<PasswordLoginResponse, LoginError> {
22    use bitwarden_crypto::EncString;
23
24    use crate::{client::UserLoginMethod, require};
25
26    info!("password logging in");
27
28    let kdf = client.auth().prelogin(input.email.clone()).await?;
29
30    let master_password_authentication =
31        MasterPasswordAuthenticationData::derive(&input.password, &kdf, &input.email)?;
32
33    let password_hash = master_password_authentication
34        .master_password_authentication_hash
35        .to_string();
36
37    let response = request_identity_tokens(client, input, &password_hash).await?;
38
39    if let IdentityTokenResponse::Authenticated(r) = &response {
40        use crate::key_management::account_cryptographic_state::WrappedAccountCryptographicState;
41
42        client.internal.set_tokens(
43            r.access_token.clone(),
44            r.refresh_token.clone(),
45            r.expires_in,
46        );
47
48        let private_key: EncString = require!(&r.private_key).parse()?;
49
50        let user_key_state = WrappedAccountCryptographicState::V1 { private_key };
51
52        let master_password_unlock = r
53            .user_decryption_options
54            .as_ref()
55            .map(UserDecryptionData::try_from)
56            .transpose()?
57            .and_then(|user_decryption| user_decryption.master_password_unlock);
58        if let Some(master_password_unlock) = master_password_unlock {
59            client
60                .internal
61                .initialize_user_crypto_master_password_unlock(
62                    input.password.clone(),
63                    master_password_unlock.clone(),
64                    user_key_state,
65                )?;
66
67            client
68                .internal
69                .set_login_method(LoginMethod::User(UserLoginMethod::Username {
70                    client_id: "web".to_owned(),
71                    email: master_password_unlock.salt,
72                    kdf: master_password_unlock.kdf,
73                }));
74        }
75    }
76
77    Ok(PasswordLoginResponse::process_response(response))
78}
79
80#[cfg(feature = "internal")]
81async fn request_identity_tokens(
82    client: &Client,
83    input: &PasswordLoginRequest,
84    password_hash: &str,
85) -> Result<IdentityTokenResponse, LoginError> {
86    use crate::DeviceType;
87
88    let config = client.internal.get_api_configurations().await;
89    PasswordTokenRequest::new(
90        &input.email,
91        password_hash,
92        DeviceType::ChromeBrowser,
93        "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33",
94        &input.two_factor,
95    )
96    .send(&config)
97    .await
98}
99
100/// Login to Bitwarden with Username and Password
101#[cfg(feature = "internal")]
102#[derive(Serialize, Deserialize, Debug, JsonSchema)]
103#[serde(rename_all = "camelCase", deny_unknown_fields)]
104pub struct PasswordLoginRequest {
105    /// Bitwarden account email address
106    pub email: String,
107    /// Bitwarden account master password
108    pub password: String,
109    /// Two-factor authentication
110    pub two_factor: Option<TwoFactorRequest>,
111}
112
113#[allow(missing_docs)]
114#[derive(Serialize, Deserialize, Debug, JsonSchema)]
115#[serde(rename_all = "camelCase", deny_unknown_fields)]
116pub struct PasswordLoginResponse {
117    pub authenticated: bool,
118    /// TODO: What does this do?
119    pub reset_master_password: bool,
120    /// Whether or not the user is required to update their master password
121    pub force_password_reset: bool,
122    /// The available two factor authentication options. Present only when authentication fails due
123    /// to requiring a second authentication factor.
124    pub two_factor: Option<TwoFactorProviders>,
125}
126
127impl PasswordLoginResponse {
128    pub(crate) fn process_response(response: IdentityTokenResponse) -> PasswordLoginResponse {
129        match response {
130            IdentityTokenResponse::Authenticated(success) => PasswordLoginResponse {
131                authenticated: true,
132                reset_master_password: success.reset_master_password,
133                force_password_reset: success.force_password_reset,
134                two_factor: None,
135            },
136            IdentityTokenResponse::Payload(_) => PasswordLoginResponse {
137                authenticated: true,
138                reset_master_password: false,
139                force_password_reset: false,
140                two_factor: None,
141            },
142            IdentityTokenResponse::TwoFactorRequired(two_factor) => PasswordLoginResponse {
143                authenticated: false,
144                reset_master_password: false,
145                force_password_reset: false,
146                two_factor: Some(two_factor.two_factor_providers.into()),
147            },
148            IdentityTokenResponse::Refreshed(_) => {
149                unreachable!("Got a `refresh_token` answer to a login request")
150            }
151        }
152    }
153}