bitwarden_core/auth/login/
password.rs1#[cfg(feature = "internal")]
2use log::info;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
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::{
25 client::{UserLoginMethod, internal::UserKeyState},
26 require,
27 };
28
29 info!("password logging in");
30
31 let kdf = client.auth().prelogin(input.email.clone()).await?;
32
33 let master_password_authentication =
34 MasterPasswordAuthenticationData::derive(&input.password, &kdf, &input.email)?;
35
36 let password_hash = master_password_authentication
37 .master_password_authentication_hash
38 .to_string();
39
40 let response = request_identity_tokens(client, input, &password_hash).await?;
41
42 if let IdentityTokenResponse::Authenticated(r) = &response {
43 client.internal.set_tokens(
44 r.access_token.clone(),
45 r.refresh_token.clone(),
46 r.expires_in,
47 );
48
49 let private_key: EncString = require!(&r.private_key).parse()?;
50
51 let user_key_state = UserKeyState {
52 private_key,
53 signing_key: None,
54 security_state: None,
55 };
56
57 let master_password_unlock = r
58 .user_decryption_options
59 .as_ref()
60 .map(UserDecryptionData::try_from)
61 .transpose()?
62 .and_then(|user_decryption| user_decryption.master_password_unlock);
63 if let Some(master_password_unlock) = master_password_unlock {
64 client
65 .internal
66 .initialize_user_crypto_master_password_unlock(
67 input.password.clone(),
68 master_password_unlock.clone(),
69 user_key_state,
70 )?;
71
72 client
73 .internal
74 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
75 client_id: "web".to_owned(),
76 email: master_password_unlock.salt,
77 kdf: master_password_unlock.kdf,
78 }));
79 }
80 }
81
82 Ok(PasswordLoginResponse::process_response(response))
83}
84
85#[cfg(feature = "internal")]
86async fn request_identity_tokens(
87 client: &Client,
88 input: &PasswordLoginRequest,
89 password_hash: &str,
90) -> Result<IdentityTokenResponse, LoginError> {
91 use crate::DeviceType;
92
93 let config = client.internal.get_api_configurations().await;
94 PasswordTokenRequest::new(
95 &input.email,
96 password_hash,
97 DeviceType::ChromeBrowser,
98 "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33",
99 &input.two_factor,
100 )
101 .send(&config)
102 .await
103}
104
105#[cfg(feature = "internal")]
107#[derive(Serialize, Deserialize, Debug, JsonSchema)]
108#[serde(rename_all = "camelCase", deny_unknown_fields)]
109pub struct PasswordLoginRequest {
110 pub email: String,
112 pub password: String,
114 pub two_factor: Option<TwoFactorRequest>,
116}
117
118#[allow(missing_docs)]
119#[derive(Serialize, Deserialize, Debug, JsonSchema)]
120#[serde(rename_all = "camelCase", deny_unknown_fields)]
121pub struct PasswordLoginResponse {
122 pub authenticated: bool,
123 pub reset_master_password: bool,
125 pub force_password_reset: bool,
127 pub two_factor: Option<TwoFactorProviders>,
130}
131
132impl PasswordLoginResponse {
133 pub(crate) fn process_response(response: IdentityTokenResponse) -> PasswordLoginResponse {
134 match response {
135 IdentityTokenResponse::Authenticated(success) => PasswordLoginResponse {
136 authenticated: true,
137 reset_master_password: success.reset_master_password,
138 force_password_reset: success.force_password_reset,
139 two_factor: None,
140 },
141 IdentityTokenResponse::Payload(_) => PasswordLoginResponse {
142 authenticated: true,
143 reset_master_password: false,
144 force_password_reset: false,
145 two_factor: None,
146 },
147 IdentityTokenResponse::TwoFactorRequired(two_factor) => PasswordLoginResponse {
148 authenticated: false,
149 reset_master_password: false,
150 force_password_reset: false,
151 two_factor: Some(two_factor.two_factor_providers.into()),
152 },
153 IdentityTokenResponse::Refreshed(_) => {
154 unreachable!("Got a `refresh_token` answer to a login request")
155 }
156 }
157 }
158}