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, ClientSettings, 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 with settings
38//! let settings = ClientSettings {
39//! identity_url: "https://identity.bitwarden.com".to_string(),
40//! api_url: "https://api.bitwarden.com".to_string(),
41//! user_agent: "MyApp/1.0".to_string(),
42//! device_type: DeviceType::SDK,
43//! device_identifier: None,
44//! bitwarden_client_version: None,
45//! bitwarden_package_type: None,
46//! };
47//! let login_client = auth_client.login(settings);
48//!
49//! // Step 1: Get user's KDF configuration
50//! let prelogin = login_client
51//! .get_password_prelogin("[email protected]".to_string())
52//! .await?;
53//!
54//! // Step 2: Construct and send login request
55//! let response = login_client.login_via_password(PasswordLoginRequest {
56//! login_request: LoginRequest {
57//! client_id: "connector".to_string(),
58//! device: LoginDeviceRequest {
59//! device_type: DeviceType::SDK,
60//! device_identifier: "device-id".to_string(),
61//! device_name: "My Device".to_string(),
62//! device_push_token: None,
63//! },
64//! },
65//! email: "[email protected]".to_string(),
66//! password: "master-password".to_string(),
67//! prelogin_response: prelogin,
68//! }).await?;
69//!
70//! // Step 3: Use tokens from response for authenticated requests
71//! match response {
72//! LoginResponse::Authenticated(success) => {
73//! let access_token = success.access_token;
74//! // Use access_token for authenticated requests
75//! }
76//! }
77//! # Ok(())
78//! # }
79//! ```
80//!
81//! [`LoginClient::get_password_prelogin`]: crate::login::LoginClient::get_password_prelogin
82//! [`LoginClient::login_via_password`]: crate::login::LoginClient::login_via_password
83
84mod login_via_password_impl;
85mod password_login_api_request;
86mod password_login_request;
87mod password_prelogin;
88
89pub(crate) use password_login_api_request::PasswordLoginApiRequest;
90pub use password_login_request::PasswordLoginRequest;
91pub use password_prelogin::PasswordPreloginError;
92
93mod password_prelogin_response;
94pub use password_prelogin_response::PasswordPreloginResponse;
95
96mod password_login_error;
97pub use password_login_error::PasswordLoginError;