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