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 )?;
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#[cfg(feature = "internal")]
102#[derive(Serialize, Deserialize, Debug, JsonSchema)]
103#[serde(rename_all = "camelCase", deny_unknown_fields)]
104pub struct PasswordLoginRequest {
105 pub email: String,
107 pub password: String,
109 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 pub reset_master_password: bool,
120 pub force_password_reset: bool,
122 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}