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