bitwarden_core/auth/login/
password.rs

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