bitwarden_core/auth/login/
auth_request.rs1use bitwarden_api_api::{
2 apis::auth_requests_api::{auth_requests_id_response_get, auth_requests_post},
3 models::{AuthRequestCreateRequestModel, AuthRequestType},
4};
5use bitwarden_crypto::Kdf;
6use uuid::Uuid;
7
8use super::LoginError;
9use crate::{
10 auth::{
11 api::{request::AuthRequestTokenRequest, response::IdentityTokenResponse},
12 auth_request::new_auth_request,
13 },
14 client::{LoginMethod, UserLoginMethod},
15 key_management::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
16 require, ApiError, Client,
17};
18
19#[allow(missing_docs)]
20pub struct NewAuthRequestResponse {
21 pub fingerprint: String,
22 email: String,
23 device_identifier: String,
24 auth_request_id: Uuid,
25 access_code: String,
26 private_key: String,
27}
28
29pub(crate) async fn send_new_auth_request(
30 client: &Client,
31 email: String,
32 device_identifier: String,
33) -> Result<NewAuthRequestResponse, LoginError> {
34 let config = client.internal.get_api_configurations().await;
35
36 let auth = new_auth_request(&email)?;
37
38 let req = AuthRequestCreateRequestModel {
39 email: email.clone(),
40 public_key: auth.public_key,
41 device_identifier: device_identifier.clone(),
42 access_code: auth.access_code.clone(),
43 r#type: AuthRequestType::AuthenticateAndUnlock,
44 };
45
46 let res = auth_requests_post(&config.api, Some(req))
47 .await
48 .map_err(ApiError::from)?;
49
50 Ok(NewAuthRequestResponse {
51 fingerprint: auth.fingerprint,
52 email,
53 device_identifier,
54 auth_request_id: require!(res.id),
55 access_code: auth.access_code,
56 private_key: auth.private_key,
57 })
58}
59
60pub(crate) async fn complete_auth_request(
61 client: &Client,
62 auth_req: NewAuthRequestResponse,
63) -> Result<(), LoginError> {
64 let config = client.internal.get_api_configurations().await;
65
66 let res = auth_requests_id_response_get(
67 &config.api,
68 auth_req.auth_request_id,
69 Some(&auth_req.access_code),
70 )
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 method: InitUserCryptoMethod::AuthRequest {
125 request_private_key: auth_req.private_key,
126 method,
127 },
128 })
129 .await?;
130
131 Ok(())
132 } else {
133 Err(LoginError::AuthenticationFailed)
134 }
135}