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