Skip to main content

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                    &None,
52                )?;
53
54            client
55                .internal
56                .set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
57                    client_id: input.client_id.clone(),
58                    client_secret: input.client_secret.clone(),
59                    email: master_password_unlock.salt,
60                    kdf: master_password_unlock.kdf,
61                }));
62        }
63    }
64
65    Ok(ApiKeyLoginResponse::process_response(response))
66}
67
68async fn request_api_identity_tokens(
69    client: &Client,
70    input: &ApiKeyLoginRequest,
71) -> Result<IdentityTokenResponse, LoginError> {
72    let config = client.internal.get_api_configurations();
73    ApiTokenRequest::new(&input.client_id, &input.client_secret)
74        .send(&config.identity_config)
75        .await
76}
77
78/// Login to Bitwarden with Api Key
79#[derive(Serialize, Deserialize, Debug, JsonSchema)]
80#[serde(rename_all = "camelCase", deny_unknown_fields)]
81pub struct ApiKeyLoginRequest {
82    /// Bitwarden account client_id
83    pub client_id: String,
84    /// Bitwarden account client_secret
85    pub client_secret: String,
86
87    /// Bitwarden account master password
88    pub password: String,
89}
90
91#[allow(missing_docs)]
92#[derive(Serialize, Deserialize, Debug, JsonSchema)]
93#[serde(rename_all = "camelCase", deny_unknown_fields)]
94pub struct ApiKeyLoginResponse {
95    pub authenticated: bool,
96    /// TODO: What does this do?
97    pub reset_master_password: bool,
98    /// Whether or not the user is required to update their master password
99    pub force_password_reset: bool,
100    two_factor: Option<TwoFactorProviders>,
101}
102
103impl ApiKeyLoginResponse {
104    pub(crate) fn process_response(response: IdentityTokenResponse) -> ApiKeyLoginResponse {
105        let password_response = PasswordLoginResponse::process_response(response);
106
107        ApiKeyLoginResponse {
108            authenticated: password_response.authenticated,
109            reset_master_password: password_response.reset_master_password,
110            force_password_reset: password_response.force_password_reset,
111            two_factor: password_response.two_factor,
112        }
113    }
114}