bitwarden_core/auth/login/
password.rs1#[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#[cfg(feature = "internal")]
91#[derive(Serialize, Deserialize, Debug, JsonSchema)]
92#[serde(rename_all = "camelCase", deny_unknown_fields)]
93pub struct PasswordLoginRequest {
94 pub email: String,
96 pub password: String,
98 pub two_factor: Option<TwoFactorRequest>,
100 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 pub reset_master_password: bool,
111 pub force_password_reset: bool,
113 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}