bitwarden_core/platform/
get_user_api_key.rs1use std::sync::Arc;
16
17use bitwarden_api_api::models::{ApiKeyResponseModel, SecretVerificationRequestModel};
18use bitwarden_crypto::{CryptoError, HashPurpose, MasterKey};
19use log::{debug, info};
20use serde::{Deserialize, Serialize};
21use thiserror::Error;
22
23use super::SecretVerificationRequest;
24use crate::{
25 ApiError, Client, MissingFieldError, NotAuthenticatedError,
26 client::{LoginMethod, UserLoginMethod},
27 require,
28};
29
30#[allow(missing_docs)]
31#[derive(Debug, Error)]
32pub enum UserApiKeyError {
33 #[error(transparent)]
34 Api(#[from] ApiError),
35 #[error(transparent)]
36 Crypto(#[from] bitwarden_crypto::CryptoError),
37 #[error(transparent)]
38 NotAuthenticated(#[from] NotAuthenticatedError),
39 #[error(transparent)]
40 MissingField(#[from] MissingFieldError),
41 #[error("Unsupported login method")]
42 UnsupportedLoginMethod,
43}
44
45pub(crate) async fn get_user_api_key(
47 client: &Client,
48 input: &SecretVerificationRequest,
49) -> Result<UserApiKeyResponse, UserApiKeyError> {
50 info!("Getting Api Key");
51 debug!("{input:?}");
52
53 let auth_settings = get_login_method(client)?;
54 let config = client.internal.get_api_configurations().await;
55
56 let request = build_secret_verification_request(&auth_settings, input)?;
57 let response = config
58 .api_client
59 .accounts_api()
60 .api_key(Some(request))
61 .await
62 .map_err(ApiError::from)?;
63 UserApiKeyResponse::process_response(response)
64}
65
66fn get_login_method(client: &Client) -> Result<Arc<LoginMethod>, NotAuthenticatedError> {
67 client
68 .internal
69 .get_login_method()
70 .ok_or(NotAuthenticatedError)
71}
72
73fn build_secret_verification_request(
75 login_method: &LoginMethod,
76 input: &SecretVerificationRequest,
77) -> Result<SecretVerificationRequestModel, UserApiKeyError> {
78 if let LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) = login_method {
79 let master_password_hash = input
80 .master_password
81 .as_ref()
82 .map(|p| {
83 let master_key = MasterKey::derive(p, email, kdf)?;
84
85 Ok::<String, CryptoError>(
86 master_key
87 .derive_master_key_hash(p.as_bytes(), HashPurpose::ServerAuthorization)
88 .to_string(),
89 )
90 })
91 .transpose()?;
92 Ok(SecretVerificationRequestModel {
93 master_password_hash,
94 otp: input.otp.as_ref().cloned(),
95 secret: None,
96 auth_request_access_code: None,
97 })
98 } else {
99 Err(UserApiKeyError::UnsupportedLoginMethod)
100 }
101}
102
103#[derive(Serialize, Deserialize, Debug)]
105#[serde(rename_all = "camelCase", deny_unknown_fields)]
106pub struct UserApiKeyResponse {
107 api_key: String,
109}
110
111impl UserApiKeyResponse {
112 pub(crate) fn process_response(
113 response: ApiKeyResponseModel,
114 ) -> Result<UserApiKeyResponse, UserApiKeyError> {
115 let api_key = require!(response.api_key);
116 Ok(UserApiKeyResponse { api_key })
117 }
118}