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, PasswordLoginResponse},
JWTToken,
},
client::{LoginMethod, UserLoginMethod},
error::Result,
require, Client,
};
pub(crate) async fn login_api_key(
client: &Client,
input: &ApiKeyLoginRequest,
) -> Result<ApiKeyLoginResponse> {
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("Access token doesn't contain email")?;
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)?;
}
ApiKeyLoginResponse::process_response(response)
}
async fn request_api_identity_tokens(
client: &Client,
input: &ApiKeyLoginRequest,
) -> Result<IdentityTokenResponse> {
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) -> Result<ApiKeyLoginResponse> {
let password_response = PasswordLoginResponse::process_response(response)?;
Ok(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,
})
}
}