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::{
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
47/// Get the user's API key.
48pub(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
72/// Build the secret verification request.
73fn 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/// The response from the server when requesting the user's API key.
99#[derive(Serialize, Deserialize, Debug)]
100#[serde(rename_all = "camelCase", deny_unknown_fields)]
101pub struct UserApiKeyResponse {
102    /// The user's API key, which represents the client_secret portion of an oauth request.
103    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}