bitwarden_core/auth/login/
password.rs1use 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 &None,
66 )?;
67
68 client
69 .internal
70 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
71 client_id: "web".to_owned(),
72 email: master_password_unlock.salt,
73 kdf: master_password_unlock.kdf,
74 }));
75 }
76 }
77
78 Ok(PasswordLoginResponse::process_response(response))
79}
80
81#[cfg(feature = "internal")]
82async fn request_identity_tokens(
83 client: &Client,
84 input: &PasswordLoginRequest,
85 password_hash: &str,
86) -> Result<IdentityTokenResponse, LoginError> {
87 use crate::DeviceType;
88
89 let config = client.internal.get_api_configurations();
90 PasswordTokenRequest::new(
91 &input.email,
92 password_hash,
93 DeviceType::ChromeBrowser,
94 "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33",
95 &input.two_factor,
96 )
97 .send(&config.identity_config)
98 .await
99}
100
101#[cfg(feature = "internal")]
103#[derive(Serialize, Deserialize, Debug, JsonSchema)]
104#[serde(rename_all = "camelCase", deny_unknown_fields)]
105pub struct PasswordLoginRequest {
106 pub email: String,
108 pub password: String,
110 pub two_factor: Option<TwoFactorRequest>,
112}
113
114#[allow(missing_docs)]
115#[derive(Serialize, Deserialize, Debug, JsonSchema)]
116#[serde(rename_all = "camelCase", deny_unknown_fields)]
117pub struct PasswordLoginResponse {
118 pub authenticated: bool,
119 pub reset_master_password: bool,
121 pub force_password_reset: bool,
123 pub two_factor: Option<TwoFactorProviders>,
126}
127
128impl PasswordLoginResponse {
129 pub(crate) fn process_response(response: IdentityTokenResponse) -> PasswordLoginResponse {
130 match response {
131 IdentityTokenResponse::Authenticated(success) => PasswordLoginResponse {
132 authenticated: true,
133 reset_master_password: success.reset_master_password,
134 force_password_reset: success.force_password_reset,
135 two_factor: None,
136 },
137 IdentityTokenResponse::Payload(_) => PasswordLoginResponse {
138 authenticated: true,
139 reset_master_password: false,
140 force_password_reset: false,
141 two_factor: None,
142 },
143 IdentityTokenResponse::TwoFactorRequired(two_factor) => PasswordLoginResponse {
144 authenticated: false,
145 reset_master_password: false,
146 force_password_reset: false,
147 two_factor: Some(two_factor.two_factor_providers.into()),
148 },
149 IdentityTokenResponse::Refreshed(_) => {
150 unreachable!("Got a `refresh_token` answer to a login request")
151 }
152 }
153 }
154}