Skip to main content

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();
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();
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.identity_config)
91    .await?;
92
93    if let IdentityTokenResponse::Authenticated(r) = response {
94        client
95            .internal
96            .set_tokens(
97                r.access_token.clone(),
98                r.refresh_token.clone(),
99                r.expires_in,
100            )
101            .await;
102
103        let method = match res.master_password_hash {
104            Some(_) => AuthRequestMethod::MasterKey {
105                protected_master_key: require!(res.key).parse()?,
106                auth_request_key: require!(r.key).parse()?,
107            },
108            None => AuthRequestMethod::UserKey {
109                protected_user_key: require!(res.key).parse()?,
110            },
111        };
112
113        let master_password_unlock = r
114            .user_decryption_options
115            .as_ref()
116            .map(UserDecryptionData::try_from)
117            .transpose()?
118            .and_then(|user_decryption| user_decryption.master_password_unlock);
119        let kdf = master_password_unlock
120            .as_ref()
121            .map(|mpu| mpu.kdf.clone())
122            .unwrap_or_else(Kdf::default_pbkdf2);
123        let salt = master_password_unlock
124            .as_ref()
125            .map(|mpu| mpu.salt.clone())
126            .unwrap_or_else(|| auth_req.email.clone());
127
128        client
129            .crypto()
130            .initialize_user_crypto(InitUserCryptoRequest {
131                user_id: None,
132                kdf_params: kdf,
133                email: salt,
134                account_cryptographic_state: WrappedAccountCryptographicState::V1 {
135                    private_key: require!(r.private_key).parse()?,
136                },
137                method: InitUserCryptoMethod::AuthRequest {
138                    request_private_key: auth_req.private_key,
139                    method,
140                },
141                upgrade_token: None,
142            })
143            .await?;
144
145        Ok(())
146    } else {
147        Err(LoginError::AuthenticationFailed)
148    }
149}