bitwarden_core/auth/login/
auth_request.rs

1use 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}