bitwarden_core/auth/login/
auth_request.rs

1use 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    mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
16    require, ApiError, Client,
17};
18
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: String,
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,
40        device_identifier: device_identifier.clone(),
41        access_code: auth.access_code.clone(),
42        r#type: AuthRequestType::AuthenticateAndUnlock,
43    };
44
45    let res = auth_requests_post(&config.api, Some(req))
46        .await
47        .map_err(ApiError::from)?;
48
49    Ok(NewAuthRequestResponse {
50        fingerprint: auth.fingerprint,
51        email,
52        device_identifier,
53        auth_request_id: require!(res.id),
54        access_code: auth.access_code,
55        private_key: auth.private_key,
56    })
57}
58
59pub(crate) async fn complete_auth_request(
60    client: &Client,
61    auth_req: NewAuthRequestResponse,
62) -> Result<(), LoginError> {
63    let config = client.internal.get_api_configurations().await;
64
65    let res = auth_requests_id_response_get(
66        &config.api,
67        auth_req.auth_request_id,
68        Some(&auth_req.access_code),
69    )
70    .await
71    .map_err(ApiError::from)?;
72
73    let approved = res.request_approved.unwrap_or(false);
74
75    if !approved {
76        return Err(LoginError::AuthRequestNotApproved);
77    }
78
79    let response = AuthRequestTokenRequest::new(
80        &auth_req.email,
81        &auth_req.auth_request_id,
82        &auth_req.access_code,
83        config.device_type,
84        &auth_req.device_identifier,
85    )
86    .send(&config)
87    .await?;
88
89    if let IdentityTokenResponse::Authenticated(r) = response {
90        let kdf = Kdf::default();
91
92        client.internal.set_tokens(
93            r.access_token.clone(),
94            r.refresh_token.clone(),
95            r.expires_in,
96        );
97        client
98            .internal
99            .set_login_method(LoginMethod::User(UserLoginMethod::Username {
100                client_id: "web".to_owned(),
101                email: auth_req.email.to_owned(),
102                kdf: kdf.clone(),
103            }));
104
105        let method = match res.master_password_hash {
106            Some(_) => AuthRequestMethod::MasterKey {
107                protected_master_key: require!(res.key).parse()?,
108                auth_request_key: require!(r.key).parse()?,
109            },
110            None => AuthRequestMethod::UserKey {
111                protected_user_key: require!(res.key).parse()?,
112            },
113        };
114
115        client
116            .crypto()
117            .initialize_user_crypto(InitUserCryptoRequest {
118                kdf_params: kdf,
119                email: auth_req.email,
120                private_key: require!(r.private_key),
121                method: InitUserCryptoMethod::AuthRequest {
122                    request_private_key: auth_req.private_key,
123                    method,
124                },
125            })
126            .await?;
127
128        Ok(())
129    } else {
130        Err(LoginError::AuthenticationFailed)
131    }
132}