bitwarden_auth/login/login_via_password/mod.rs
1//! Password-based authentication for Bitwarden users.
2//!
3//! This module implements the password login flow, which requires two steps:
4//!
5//! 1. **Prelogin**: Retrieve the user's KDF configuration with
6//! [`LoginClient::get_password_prelogin`]
7//! 2. **Login**: Authenticate with [`LoginClient::login_via_password`] using the KDF settings
8//!
9//! # Security Model
10//!
11//! The master password is **never sent to the server**. Instead:
12//! - User's KDF settings (PBKDF2 or Argon2id) are fetched during prelogin
13//! - Master password is stretched with KDF to derive the master key
14//! - Master key is stretched again into an AES256-CBC-HMAC key to unwrap the user key
15//! - Master key is hashed with single-round PBKDF2 (using password as salt) to create the server
16//! authentication hash
17//! - Only the authentication hash is transmitted to the server
18//! - All requests include no-cache headers to prevent sensitive data caching
19//!
20//! # Current Limitations
21//!
22//! - Two-factor authentication (2FA) not yet supported
23//! - New device verification not yet implemented
24//!
25//! # Complete Example
26//!
27//! ```rust,no_run
28//! # use bitwarden_auth::{AuthClient, AuthClientExt};
29//! # use bitwarden_auth::login::login_via_password::PasswordLoginRequest;
30//! # use bitwarden_auth::login::models::{LoginRequest, LoginDeviceRequest, LoginResponse};
31//! # use bitwarden_core::{Client, DeviceType};
32//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
33//! // Create the core client
34//! let client = Client::new(None);
35//! let auth_client = AuthClient::new(client);
36//!
37//! // Create login client, sharing the same backing client as the auth client
38//! let login_client = auth_client.login();
39//!
40//! // Step 1: Get user's KDF configuration
41//! let prelogin = login_client
42//! .get_password_prelogin("[email protected]".to_string())
43//! .await?;
44//!
45//! // Step 2: Construct and send login request
46//! let response = login_client.login_via_password(PasswordLoginRequest {
47//! login_request: LoginRequest {
48//! client_id: "connector".to_string(),
49//! device: LoginDeviceRequest {
50//! device_type: DeviceType::SDK,
51//! device_identifier: "device-id".to_string(),
52//! device_name: "My Device".to_string(),
53//! device_push_token: None,
54//! },
55//! },
56//! email: "[email protected]".to_string(),
57//! password: "master-password".to_string(),
58//! prelogin_response: prelogin,
59//! }).await?;
60//!
61//! // Step 3: Use tokens from response for authenticated requests
62//! match response {
63//! LoginResponse::Authenticated(success) => {
64//! let access_token = success.access_token;
65//! // Use access_token for authenticated requests
66//! }
67//! }
68//! # Ok(())
69//! # }
70//! ```
71//!
72//! [`LoginClient::get_password_prelogin`]: crate::login::LoginClient::get_password_prelogin
73//! [`LoginClient::login_via_password`]: crate::login::LoginClient::login_via_password
74
75mod login_via_password_impl;
76mod password_login_api_request;
77mod password_login_request;
78mod password_prelogin;
79
80pub(crate) use password_login_api_request::PasswordLoginApiRequest;
81pub use password_login_request::PasswordLoginRequest;
82pub use password_prelogin::PasswordPreloginError;
83
84mod password_prelogin_response;
85pub use password_prelogin_response::PasswordPreloginResponse;
86
87mod password_login_error;
88pub use password_login_error::PasswordLoginError;