bitwarden_core/auth/login/
auth_request.rs

1use bitwarden_api_api::{
2    apis::auth_requests_api::{auth_requests_id_response_get, auth_requests_post},
3    models::{AuthRequestCreateRequestModel, AuthRequestType},
4};
5use bitwarden_crypto::Kdf;
6use bitwarden_encoding::B64;
7use uuid::Uuid;
8
9use super::LoginError;
10use crate::{
11    auth::{
12        api::{request::AuthRequestTokenRequest, response::IdentityTokenResponse},
13        auth_request::new_auth_request,
14    },
15    client::{LoginMethod, UserLoginMethod},
16    key_management::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
17    require, ApiError, Client,
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 = auth_requests_post(&config.api, Some(req))
48        .await
49        .map_err(ApiError::from)?;
50
51    Ok(NewAuthRequestResponse {
52        fingerprint: auth.fingerprint,
53        email,
54        device_identifier,
55        auth_request_id: require!(res.id),
56        access_code: auth.access_code,
57        private_key: auth.private_key,
58    })
59}
60
61pub(crate) async fn complete_auth_request(
62    client: &Client,
63    auth_req: NewAuthRequestResponse,
64) -> Result<(), LoginError> {
65    let config = client.internal.get_api_configurations().await;
66
67    let res = auth_requests_id_response_get(
68        &config.api,
69        auth_req.auth_request_id,
70        Some(&auth_req.access_code),
71    )
72    .await
73    .map_err(ApiError::from)?;
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)
89    .await?;
90
91    if let IdentityTokenResponse::Authenticated(r) = response {
92        let kdf = Kdf::default();
93
94        client.internal.set_tokens(
95            r.access_token.clone(),
96            r.refresh_token.clone(),
97            r.expires_in,
98        );
99        client
100            .internal
101            .set_login_method(LoginMethod::User(UserLoginMethod::Username {
102                client_id: "web".to_owned(),
103                email: auth_req.email.to_owned(),
104                kdf: kdf.clone(),
105            }));
106
107        let method = match res.master_password_hash {
108            Some(_) => AuthRequestMethod::MasterKey {
109                protected_master_key: require!(res.key).parse()?,
110                auth_request_key: require!(r.key).parse()?,
111            },
112            None => AuthRequestMethod::UserKey {
113                protected_user_key: require!(res.key).parse()?,
114            },
115        };
116
117        client
118            .crypto()
119            .initialize_user_crypto(InitUserCryptoRequest {
120                user_id: None,
121                kdf_params: kdf,
122                email: auth_req.email,
123                private_key: require!(r.private_key).parse()?,
124                signing_key: None,
125                security_state: None,
126                method: InitUserCryptoMethod::AuthRequest {
127                    request_private_key: auth_req.private_key,
128                    method,
129                },
130            })
131            .await?;
132
133        Ok(())
134    } else {
135        Err(LoginError::AuthenticationFailed)
136    }
137}