bitwarden_core/platform/
get_user_api_key.rs

1//! Get the user's API key.
2//!
3//! This module provides the functionality to get the user's API key.
4//!
5//! <div class="warning">
6//!
7//! This code is currently unused and unmaintained!
8//!
9//! - Prior to use it should be reviewed and tested.
10//! - Code should move to the appropriate code owner.
11//! - Secret verification should be extracted as it's a common pattern for multiple requests.
12//!
13//! </div>
14
15use 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
45/// Get the user's API key.
46pub(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
73/// Build the secret verification request.
74fn 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/// The response from the server when requesting the user's API key.
104#[derive(Serialize, Deserialize, Debug)]
105#[serde(rename_all = "camelCase", deny_unknown_fields)]
106pub struct UserApiKeyResponse {
107    /// The user's API key, which represents the client_secret portion of an oauth request.
108    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}