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