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