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 &None,
52 )?;
53
54 client
55 .internal
56 .set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
57 client_id: input.client_id.clone(),
58 client_secret: input.client_secret.clone(),
59 email: master_password_unlock.salt,
60 kdf: master_password_unlock.kdf,
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();
73 ApiTokenRequest::new(&input.client_id, &input.client_secret)
74 .send(&config.identity_config)
75 .await
76}
77
78#[derive(Serialize, Deserialize, Debug, JsonSchema)]
80#[serde(rename_all = "camelCase", deny_unknown_fields)]
81pub struct ApiKeyLoginRequest {
82 pub client_id: String,
84 pub client_secret: String,
86
87 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 pub reset_master_password: bool,
98 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}