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