bitwarden_core/auth/login/
api_key.rs1use 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 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#[derive(Serialize, Deserialize, Debug, JsonSchema)]
79#[serde(rename_all = "camelCase", deny_unknown_fields)]
80pub struct ApiKeyLoginRequest {
81 pub client_id: String,
83 pub client_secret: String,
85
86 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 pub reset_master_password: bool,
97 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}