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
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#[derive(Serialize, Deserialize, Debug, JsonSchema)]
84#[serde(rename_all = "camelCase", deny_unknown_fields)]
85pub struct ApiKeyLoginRequest {
86 pub client_id: String,
88 pub client_secret: String,
90
91 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 pub reset_master_password: bool,
102 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}