bitwarden_core/auth/login/
api_key.rs

1use bitwarden_crypto::EncString;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    Client,
7    auth::{
8        api::{request::ApiTokenRequest, response::IdentityTokenResponse},
9        login::{LoginError, PasswordLoginResponse, response::two_factor::TwoFactorProviders},
10    },
11    client::{LoginMethod, UserLoginMethod, internal::UserKeyState},
12    key_management::UserDecryptionData,
13    require,
14};
15
16pub(crate) async fn login_api_key(
17    client: &Client,
18    input: &ApiKeyLoginRequest,
19) -> Result<ApiKeyLoginResponse, LoginError> {
20    //info!("api key logging in");
21    //debug!("{:#?}, {:#?}", client, input);
22
23    let response = request_api_identity_tokens(client, input).await?;
24
25    if let IdentityTokenResponse::Authenticated(r) = &response {
26        client.internal.set_tokens(
27            r.access_token.clone(),
28            r.refresh_token.clone(),
29            r.expires_in,
30        );
31
32        let private_key: EncString = require!(&r.private_key).parse()?;
33
34        let user_key_state = UserKeyState {
35            private_key,
36            signing_key: None,
37            security_state: None,
38        };
39
40        let master_password_unlock = r
41            .user_decryption_options
42            .as_ref()
43            .map(UserDecryptionData::try_from)
44            .transpose()?
45            .and_then(|user_decryption| user_decryption.master_password_unlock);
46        if let Some(master_password_unlock) = master_password_unlock {
47            client
48                .internal
49                .initialize_user_crypto_master_password_unlock(
50                    input.password.clone(),
51                    master_password_unlock.clone(),
52                    user_key_state,
53                )?;
54
55            client
56                .internal
57                .set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
58                    client_id: input.client_id.clone(),
59                    client_secret: input.client_secret.clone(),
60                    email: master_password_unlock.salt,
61                    kdf: master_password_unlock.kdf,
62                }));
63        }
64    }
65
66    Ok(ApiKeyLoginResponse::process_response(response))
67}
68
69async fn request_api_identity_tokens(
70    client: &Client,
71    input: &ApiKeyLoginRequest,
72) -> Result<IdentityTokenResponse, LoginError> {
73    let config = client.internal.get_api_configurations().await;
74    ApiTokenRequest::new(&input.client_id, &input.client_secret)
75        .send(&config)
76        .await
77}
78
79/// Login to Bitwarden with Api Key
80#[derive(Serialize, Deserialize, Debug, JsonSchema)]
81#[serde(rename_all = "camelCase", deny_unknown_fields)]
82pub struct ApiKeyLoginRequest {
83    /// Bitwarden account client_id
84    pub client_id: String,
85    /// Bitwarden account client_secret
86    pub client_secret: String,
87
88    /// Bitwarden account master password
89    pub password: String,
90}
91
92#[allow(missing_docs)]
93#[derive(Serialize, Deserialize, Debug, JsonSchema)]
94#[serde(rename_all = "camelCase", deny_unknown_fields)]
95pub struct ApiKeyLoginResponse {
96    pub authenticated: bool,
97    /// TODO: What does this do?
98    pub reset_master_password: bool,
99    /// Whether or not the user is required to update their master password
100    pub force_password_reset: bool,
101    two_factor: Option<TwoFactorProviders>,
102}
103
104impl ApiKeyLoginResponse {
105    pub(crate) fn process_response(response: IdentityTokenResponse) -> ApiKeyLoginResponse {
106        let password_response = PasswordLoginResponse::process_response(response);
107
108        ApiKeyLoginResponse {
109            authenticated: password_response.authenticated,
110            reset_master_password: password_response.reset_master_password,
111            force_password_reset: password_response.force_password_reset,
112            two_factor: password_response.two_factor,
113        }
114    }
115}