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).await?;
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
62async fn get_login_method(client: &Client) -> Result<UserLoginMethod, NotAuthenticatedError> {
63    client
64        .internal
65        .get_login_method()
66        .await
67        .ok_or(NotAuthenticatedError)
68}
69
70/// Build the secret verification request.
71fn build_secret_verification_request(
72    login_method: &UserLoginMethod,
73    input: &SecretVerificationRequest,
74) -> Result<SecretVerificationRequestModel, UserApiKeyError> {
75    if let UserLoginMethod::Username { email, kdf, .. } = login_method {
76        let master_password_hash = input
77            .master_password
78            .as_ref()
79            .map(|p| {
80                let master_key = MasterKey::derive(p, email, kdf)?;
81
82                Ok::<String, CryptoError>(
83                    master_key
84                        .derive_master_key_hash(p.as_bytes(), HashPurpose::ServerAuthorization)
85                        .to_string(),
86                )
87            })
88            .transpose()?;
89        Ok(SecretVerificationRequestModel {
90            master_password_hash,
91            otp: input.otp.as_ref().cloned(),
92            secret: None,
93            auth_request_access_code: None,
94        })
95    } else {
96        Err(UserApiKeyError::UnsupportedLoginMethod)
97    }
98}
99
100/// The response from the server when requesting the user's API key.
101#[derive(Serialize, Deserialize, Debug)]
102#[serde(rename_all = "camelCase", deny_unknown_fields)]
103pub struct UserApiKeyResponse {
104    /// The user's API key, which represents the client_secret portion of an oauth request.
105    api_key: String,
106}
107
108impl UserApiKeyResponse {
109    pub(crate) fn process_response(
110        response: ApiKeyResponseModel,
111    ) -> Result<UserApiKeyResponse, UserApiKeyError> {
112        let api_key = require!(response.api_key);
113        Ok(UserApiKeyResponse { api_key })
114    }
115}