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, internal::UserKeyState},
12 key_management::UserDecryptionData,
13 require,
14};
15
16pub(crate) async fn login_api_key(
17 client: &Client,
18 input: &ApiKeyLoginRequest,
19) -> Result<ApiKeyLoginResponse, LoginError> {
20 let response = request_api_identity_tokens(client, input).await?;
24
25 if let IdentityTokenResponse::Authenticated(r) = &response {
26 client.internal.set_tokens(
27 r.access_token.clone(),
28 r.refresh_token.clone(),
29 r.expires_in,
30 );
31
32 let private_key: EncString = require!(&r.private_key).parse()?;
33
34 let user_key_state = UserKeyState {
35 private_key,
36 signing_key: None,
37 security_state: None,
38 };
39
40 let master_password_unlock = r
41 .user_decryption_options
42 .as_ref()
43 .map(UserDecryptionData::try_from)
44 .transpose()?
45 .and_then(|user_decryption| user_decryption.master_password_unlock);
46 if let Some(master_password_unlock) = master_password_unlock {
47 client
48 .internal
49 .initialize_user_crypto_master_password_unlock(
50 input.password.clone(),
51 master_password_unlock.clone(),
52 user_key_state,
53 )?;
54
55 client
56 .internal
57 .set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
58 client_id: input.client_id.clone(),
59 client_secret: input.client_secret.clone(),
60 email: master_password_unlock.salt,
61 kdf: master_password_unlock.kdf,
62 }));
63 }
64 }
65
66 Ok(ApiKeyLoginResponse::process_response(response))
67}
68
69async fn request_api_identity_tokens(
70 client: &Client,
71 input: &ApiKeyLoginRequest,
72) -> Result<IdentityTokenResponse, LoginError> {
73 let config = client.internal.get_api_configurations().await;
74 ApiTokenRequest::new(&input.client_id, &input.client_secret)
75 .send(&config)
76 .await
77}
78
79#[derive(Serialize, Deserialize, Debug, JsonSchema)]
81#[serde(rename_all = "camelCase", deny_unknown_fields)]
82pub struct ApiKeyLoginRequest {
83 pub client_id: String,
85 pub client_secret: String,
87
88 pub password: String,
90}
91
92#[allow(missing_docs)]
93#[derive(Serialize, Deserialize, Debug, JsonSchema)]
94#[serde(rename_all = "camelCase", deny_unknown_fields)]
95pub struct ApiKeyLoginResponse {
96 pub authenticated: bool,
97 pub reset_master_password: bool,
99 pub force_password_reset: bool,
101 two_factor: Option<TwoFactorProviders>,
102}
103
104impl ApiKeyLoginResponse {
105 pub(crate) fn process_response(response: IdentityTokenResponse) -> ApiKeyLoginResponse {
106 let password_response = PasswordLoginResponse::process_response(response);
107
108 ApiKeyLoginResponse {
109 authenticated: password_response.authenticated,
110 reset_master_password: password_response.reset_master_password,
111 force_password_reset: password_response.force_password_reset,
112 two_factor: password_response.two_factor,
113 }
114 }
115}