Skip to main content

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