bitwarden_core/auth/login/
auth_request.rs1use bitwarden_api_api::models::{AuthRequestCreateRequestModel, AuthRequestType};
2use bitwarden_crypto::Kdf;
3use bitwarden_encoding::B64;
4use uuid::Uuid;
5
6use super::LoginError;
7use crate::{
8 ApiError, Client,
9 auth::{
10 api::{request::AuthRequestTokenRequest, response::IdentityTokenResponse},
11 auth_request::new_auth_request,
12 },
13 key_management::{
14 UserDecryptionData,
15 account_cryptographic_state::WrappedAccountCryptographicState,
16 crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
17 },
18 require,
19};
20
21#[allow(missing_docs)]
22pub struct NewAuthRequestResponse {
23 pub fingerprint: String,
24 email: String,
25 device_identifier: String,
26 auth_request_id: Uuid,
27 access_code: String,
28 private_key: B64,
29}
30
31pub(crate) async fn send_new_auth_request(
32 client: &Client,
33 email: String,
34 device_identifier: String,
35) -> Result<NewAuthRequestResponse, LoginError> {
36 let config = client.internal.get_api_configurations().await;
37
38 let auth = new_auth_request(&email)?;
39
40 let req = AuthRequestCreateRequestModel {
41 email: email.clone(),
42 public_key: auth.public_key.to_string(),
43 device_identifier: device_identifier.clone(),
44 access_code: auth.access_code.clone(),
45 r#type: AuthRequestType::AuthenticateAndUnlock,
46 };
47
48 let res = config
49 .api_client
50 .auth_requests_api()
51 .post(Some(req))
52 .await
53 .map_err(ApiError::from)?;
54
55 Ok(NewAuthRequestResponse {
56 fingerprint: auth.fingerprint,
57 email,
58 device_identifier,
59 auth_request_id: require!(res.id),
60 access_code: auth.access_code,
61 private_key: auth.private_key,
62 })
63}
64
65pub(crate) async fn complete_auth_request(
66 client: &Client,
67 auth_req: NewAuthRequestResponse,
68) -> Result<(), LoginError> {
69 let config = client.internal.get_api_configurations().await;
70 let res = config
71 .api_client
72 .auth_requests_api()
73 .get_response(auth_req.auth_request_id, Some(&auth_req.access_code))
74 .await
75 .map_err(ApiError::from)?;
76
77 let approved = res.request_approved.unwrap_or(false);
78
79 if !approved {
80 return Err(LoginError::AuthRequestNotApproved);
81 }
82
83 let response = AuthRequestTokenRequest::new(
84 &auth_req.email,
85 &auth_req.auth_request_id,
86 &auth_req.access_code,
87 config.device_type,
88 &auth_req.device_identifier,
89 )
90 .send(&config)
91 .await?;
92
93 if let IdentityTokenResponse::Authenticated(r) = response {
94 client.internal.set_tokens(
95 r.access_token.clone(),
96 r.refresh_token.clone(),
97 r.expires_in,
98 );
99
100 let method = match res.master_password_hash {
101 Some(_) => AuthRequestMethod::MasterKey {
102 protected_master_key: require!(res.key).parse()?,
103 auth_request_key: require!(r.key).parse()?,
104 },
105 None => AuthRequestMethod::UserKey {
106 protected_user_key: require!(res.key).parse()?,
107 },
108 };
109
110 let master_password_unlock = r
111 .user_decryption_options
112 .as_ref()
113 .map(UserDecryptionData::try_from)
114 .transpose()?
115 .and_then(|user_decryption| user_decryption.master_password_unlock);
116 let kdf = master_password_unlock
117 .as_ref()
118 .map(|mpu| mpu.kdf.clone())
119 .unwrap_or_else(Kdf::default);
120 let salt = master_password_unlock
121 .as_ref()
122 .map(|mpu| mpu.salt.clone())
123 .unwrap_or_else(|| auth_req.email.clone());
124
125 client
126 .crypto()
127 .initialize_user_crypto(InitUserCryptoRequest {
128 user_id: None,
129 kdf_params: kdf,
130 email: salt,
131 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
132 private_key: require!(r.private_key).parse()?,
133 },
134 method: InitUserCryptoMethod::AuthRequest {
135 request_private_key: auth_req.private_key,
136 method,
137 },
138 })
139 .await?;
140
141 Ok(())
142 } else {
143 Err(LoginError::AuthenticationFailed)
144 }
145}