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::{CryptoError, 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 Ok::<String, CryptoError>(
85 master_key
86 .derive_master_key_hash(p.as_bytes(), HashPurpose::ServerAuthorization)
87 .to_string(),
88 )
89 })
90 .transpose()?;
91 Ok(SecretVerificationRequestModel {
92 master_password_hash,
93 otp: input.otp.as_ref().cloned(),
94 secret: None,
95 auth_request_access_code: None,
96 })
97 } else {
98 Err(UserApiKeyError::UnsupportedLoginMethod)
99 }
100}
101
102#[derive(Serialize, Deserialize, Debug)]
104#[serde(rename_all = "camelCase", deny_unknown_fields)]
105pub struct UserApiKeyResponse {
106 api_key: String,
108}
109
110impl UserApiKeyResponse {
111 pub(crate) fn process_response(
112 response: ApiKeyResponseModel,
113 ) -> Result<UserApiKeyResponse, UserApiKeyError> {
114 let api_key = require!(response.api_key);
115 Ok(UserApiKeyResponse { api_key })
116 }
117}