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