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    if client.internal.is_authed() {
67        client
68            .internal
69            .get_login_method()
70            .ok_or(NotAuthenticatedError)
71    } else {
72        Err(NotAuthenticatedError)
73    }
74}
75
76/// Build the secret verification request.
77fn build_secret_verification_request(
78    login_method: &LoginMethod,
79    input: &SecretVerificationRequest,
80) -> Result<SecretVerificationRequestModel, UserApiKeyError> {
81    if let LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) = login_method {
82        let master_password_hash = input
83            .master_password
84            .as_ref()
85            .map(|p| {
86                let master_key = MasterKey::derive(p, email, kdf)?;
87
88                master_key.derive_master_key_hash(p.as_bytes(), HashPurpose::ServerAuthorization)
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/// The response from the server when requesting the user's API key.
103#[derive(Serialize, Deserialize, Debug)]
104#[serde(rename_all = "camelCase", deny_unknown_fields)]
105pub struct UserApiKeyResponse {
106    /// The user's API key, which represents the client_secret portion of an oauth request.
107    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}