bitwarden_core/auth/login/
api_key.rsuse bitwarden_crypto::{EncString, MasterKey};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
auth::{
api::{request::ApiTokenRequest, response::IdentityTokenResponse},
login::{response::two_factor::TwoFactorProviders, LoginError, PasswordLoginResponse},
JwtToken,
},
client::{LoginMethod, UserLoginMethod},
require, Client,
};
pub(crate) async fn login_api_key(
client: &Client,
input: &ApiKeyLoginRequest,
) -> Result<ApiKeyLoginResponse, LoginError> {
let response = request_api_identity_tokens(client, input).await?;
if let IdentityTokenResponse::Authenticated(r) = &response {
let access_token_obj: JwtToken = r.access_token.parse()?;
let email = access_token_obj
.email
.ok_or(LoginError::JwtTokenMissingEmail)?;
let kdf = client.auth().prelogin(email.clone()).await?;
client.internal.set_tokens(
r.access_token.clone(),
r.refresh_token.clone(),
r.expires_in,
);
let master_key = MasterKey::derive(&input.password, &email, &kdf)?;
client
.internal
.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
client_id: input.client_id.to_owned(),
client_secret: input.client_secret.to_owned(),
email,
kdf,
}));
let user_key: EncString = require!(r.key.as_deref()).parse()?;
let private_key: EncString = require!(r.private_key.as_deref()).parse()?;
client
.internal
.initialize_user_crypto_master_key(master_key, user_key, private_key)?;
}
Ok(ApiKeyLoginResponse::process_response(response))
}
async fn request_api_identity_tokens(
client: &Client,
input: &ApiKeyLoginRequest,
) -> Result<IdentityTokenResponse, LoginError> {
let config = client.internal.get_api_configurations().await;
ApiTokenRequest::new(&input.client_id, &input.client_secret)
.send(&config)
.await
}
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ApiKeyLoginRequest {
pub client_id: String,
pub client_secret: String,
pub password: String,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ApiKeyLoginResponse {
pub authenticated: bool,
pub reset_master_password: bool,
pub force_password_reset: bool,
two_factor: Option<TwoFactorProviders>,
}
impl ApiKeyLoginResponse {
pub(crate) fn process_response(response: IdentityTokenResponse) -> ApiKeyLoginResponse {
let password_response = PasswordLoginResponse::process_response(response);
ApiKeyLoginResponse {
authenticated: password_response.authenticated,
reset_master_password: password_response.reset_master_password,
force_password_reset: password_response.force_password_reset,
two_factor: password_response.two_factor,
}
}
}