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