bitwarden_core/auth/login/
api_key.rs

1use bitwarden_crypto::{EncString, MasterKey};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    auth::{
7        api::{request::ApiTokenRequest, response::IdentityTokenResponse},
8        login::{response::two_factor::TwoFactorProviders, LoginError, PasswordLoginResponse},
9        JwtToken,
10    },
11    client::{internal::UserKeyState, LoginMethod, UserLoginMethod},
12    require, Client,
13};
14
15pub(crate) async fn login_api_key(
16    client: &Client,
17    input: &ApiKeyLoginRequest,
18) -> Result<ApiKeyLoginResponse, LoginError> {
19    //info!("api key logging in");
20    //debug!("{:#?}, {:#?}", client, input);
21
22    let response = request_api_identity_tokens(client, input).await?;
23
24    if let IdentityTokenResponse::Authenticated(r) = &response {
25        let access_token_obj: JwtToken = r.access_token.parse()?;
26
27        // This should always be Some() when logging in with an api key
28        let email = access_token_obj
29            .email
30            .ok_or(LoginError::JwtTokenMissingEmail)?;
31
32        let kdf = client.auth().prelogin(email.clone()).await?;
33
34        client.internal.set_tokens(
35            r.access_token.clone(),
36            r.refresh_token.clone(),
37            r.expires_in,
38        );
39
40        let master_key = MasterKey::derive(&input.password, &email, &kdf)?;
41
42        client
43            .internal
44            .set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
45                client_id: input.client_id.to_owned(),
46                client_secret: input.client_secret.to_owned(),
47                email,
48                kdf,
49            }));
50
51        let user_key: EncString = require!(r.key.as_deref()).parse()?;
52        let private_key: EncString = require!(r.private_key.as_deref()).parse()?;
53
54        client.internal.initialize_user_crypto_master_key(
55            master_key,
56            user_key,
57            UserKeyState {
58                private_key,
59                signing_key: None,
60                security_state: None,
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().await;
73    ApiTokenRequest::new(&input.client_id, &input.client_secret)
74        .send(&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}