From b2cd556f3e79673d3eff1dac9b7402c18aa69d69 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Wed, 14 Jan 2026 13:11:43 +0100 Subject: [PATCH] Fix User API Key login (#6712) When using the latest Bitwarden CLI and logging in using the API Key, it expects some extra fields, same as for normal login. This PR adds those fields and login is possible again via API Key. Fixes #6709 Signed-off-by: BlackDex --- src/api/identity.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/api/identity.rs b/src/api/identity.rs index e763ef46..722b3eab 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -610,6 +610,25 @@ async fn _user_api_key_login( info!("User {} logged in successfully via API key. IP: {}", user.email, ip.ip); + let has_master_password = !user.password_hash.is_empty(); + let master_password_unlock = if has_master_password { + json!({ + "Kdf": { + "KdfType": user.client_kdf_type, + "Iterations": user.client_kdf_iter, + "Memory": user.client_kdf_memory, + "Parallelism": user.client_kdf_parallelism + }, + // This field is named inconsistently and will be removed and replaced by the "wrapped" variant in the apps. + // https://github.com/bitwarden/android/blob/release/2025.12-rc41/network/src/main/kotlin/com/bitwarden/network/model/MasterPasswordUnlockDataJson.kt#L22-L26 + "MasterKeyEncryptedUserKey": user.akey, + "MasterKeyWrappedUserKey": user.akey, + "Salt": user.email + }) + } else { + Value::Null + }; + // Note: No refresh_token is returned. The CLI just repeats the // client_credentials login flow when the existing token expires. let result = json!({ @@ -625,6 +644,11 @@ async fn _user_api_key_login( "KdfParallelism": user.client_kdf_parallelism, "ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing "scope": AuthMethod::UserApiKey.scope(), + "UserDecryptionOptions": { + "HasMasterPassword": has_master_password, + "MasterPasswordUnlock": master_password_unlock, + "Object": "userDecryptionOptions" + }, }); Ok(Json(result))