identity.rs (15441B)
1 use crate::{ 2 api::{ 3 core::accounts::{PreloginData, RegisterData, _prelogin}, 4 ApiResult, EmptyResult, JsonResult, 5 }, 6 auth::{ClientHeaders, ClientIp}, 7 config, 8 db::{ 9 models::{Device, OrgPolicy, OrgPolicyType, TwoFactorType, User}, 10 DbConn, 11 }, 12 error::{Error, MapResult}, 13 util, 14 }; 15 use rocket::serde::json::Json; 16 use rocket::{ 17 form::{Form, FromForm}, 18 Route, 19 }; 20 use serde_json::Value; 21 22 pub fn routes() -> Vec<Route> { 23 routes![identity_register, login, prelogin] 24 } 25 26 #[post("/connect/token", data = "<data>")] 27 async fn login(data: Form<ConnectData>, client_header: ClientHeaders, conn: DbConn) -> JsonResult { 28 let data: ConnectData = data.into_inner(); 29 let mut user_uuid: Option<String> = None; 30 let login_result = match data.grant_type.as_ref() { 31 "refresh_token" => { 32 _check_is_some(&data.refresh_token, "refresh_token cannot be blank")?; 33 _refresh_login(data, &conn).await 34 } 35 "password" => { 36 _check_is_some(&data.client_id, "client_id cannot be blank")?; 37 _check_is_some(&data.password, "password cannot be blank")?; 38 _check_is_some(&data.scope, "scope cannot be blank")?; 39 _check_is_some(&data.username, "username cannot be blank")?; 40 _check_is_some(&data.device_identifier, "device_identifier cannot be blank")?; 41 _check_is_some(&data.device_name, "device_name cannot be blank")?; 42 _check_is_some(&data.device_type, "device_type cannot be blank")?; 43 _password_login(data, &mut user_uuid, &conn, &client_header.ip).await 44 } 45 t => err!("Invalid type", t), 46 }; 47 login_result 48 } 49 50 async fn _refresh_login(data: ConnectData, conn: &DbConn) -> JsonResult { 51 // Extract token 52 let token = data.refresh_token.unwrap(); 53 // Get device by refresh token 54 let mut device = Device::find_by_refresh_token(&token, conn) 55 .await 56 .map_res("Invalid refresh token")?; 57 let scope = "api offline_access"; 58 let scope_vec = vec!["api".into(), "offline_access".into()]; 59 // Common 60 let user = User::find_by_uuid(&device.user_uuid, conn).await.unwrap(); 61 // --- 62 // Disabled this variable, it was used to generate the JWT 63 // Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out 64 // See: https://github.com/dani-garcia/vaultwarden/issues/4156 65 // --- 66 // let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await; 67 let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec); 68 device.save(conn).await?; 69 let result = json!({ 70 "access_token": access_token, 71 "expires_in": expires_in, 72 "token_type": "Bearer", 73 "refresh_token": device.refresh_token, 74 "scope": scope, 75 }); 76 Ok(Json(result)) 77 } 78 #[expect(clippy::struct_excessive_bools, reason = "upstream")] 79 #[derive(Default, Deserialize, Serialize)] 80 #[serde(rename_all = "camelCase")] 81 struct MasterPasswordPolicy { 82 min_complexity: u8, 83 min_length: u32, 84 require_lower: bool, 85 require_upper: bool, 86 require_numbers: bool, 87 require_special: bool, 88 enforce_on_login: bool, 89 } 90 91 #[allow(clippy::else_if_without_else)] 92 async fn _password_login( 93 data: ConnectData, 94 user_uuid: &mut Option<String>, 95 conn: &DbConn, 96 ip: &ClientIp, 97 ) -> JsonResult { 98 // Validate scope 99 let scope = data.scope.as_ref().unwrap(); 100 if scope != "api offline_access" { 101 err!("Scope not supported") 102 } 103 let scope_vec = vec!["api".into(), "offline_access".into()]; 104 // Get the user 105 let username = data.username.as_ref().unwrap().trim(); 106 let Some(mut user) = User::find_by_mail(username, conn).await else { 107 err!( 108 "Username or password is incorrect. Try again", 109 format!("IP: {}. Username: {}.", ip.ip, username) 110 ) 111 }; 112 // Set the user_uuid here to be passed back used for event logging. 113 *user_uuid = Some(user.uuid.clone()); 114 // Check password 115 let password = data.password.as_ref().unwrap(); 116 if data.auth_request.is_some() { 117 err!( 118 "Auth request not found. Try again.", 119 format!("IP: {}. Username: {}.", ip.ip, username) 120 ) 121 } else if !user.check_valid_password(password) { 122 err!( 123 "Username or password is incorrect. Try again", 124 format!("IP: {}. Username: {}.", ip.ip, username) 125 ) 126 } 127 // Change the KDF Iterations 128 if user.password_iterations() != config::get_config().password_iterations { 129 user.set_password_iterations(config::get_config().password_iterations); 130 user.set_password(password, None, false, None); 131 if let Err(e) = user.save(conn).await { 132 panic!("Error updating user: {e:#?}"); 133 } 134 } 135 // Check if the user is disabled 136 if !user.enabled { 137 err!( 138 "This user has been disabled", 139 format!("IP: {}. Username: {}.", ip.ip, username) 140 ) 141 } 142 let (mut device, _) = get_device(&data, conn, &user).await; 143 let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec); 144 let kdf = user.client_kdf_type; 145 let kdf_iter = user.client_kdf_iter(); 146 let kdf_mem = user.client_kdf_memory(); 147 let kdf_par = user.client_kdf_parallelism(); 148 let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, conn).await?; 149 // Common 150 // --- 151 // Disabled this variable, it was used to generate the JWT 152 // Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out 153 // See: https://github.com/dani-garcia/vaultwarden/issues/4156 154 // --- 155 // let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await; 156 device.save(conn).await?; 157 // Fetch all valid Master Password Policies and merge them into one with all true's and larges numbers as one policy 158 let master_password_policies: Vec<MasterPasswordPolicy> = 159 OrgPolicy::find_accepted_and_confirmed_by_user_and_active_policy( 160 &user.uuid, 161 OrgPolicyType::MasterPassword, 162 conn, 163 ) 164 .await 165 .into_iter() 166 .filter_map(|p| serde_json::from_str(&p.data).ok()) 167 .collect(); 168 169 let master_password_policy = if master_password_policies.is_empty() { 170 json!({"object": "masterPasswordPolicy"}) 171 } else { 172 let mut mpp_json = json!(master_password_policies.into_iter().reduce(|acc, policy| { 173 MasterPasswordPolicy { 174 min_complexity: acc.min_complexity.max(policy.min_complexity), 175 min_length: acc.min_length.max(policy.min_length), 176 require_lower: acc.require_lower || policy.require_lower, 177 require_upper: acc.require_upper || policy.require_upper, 178 require_numbers: acc.require_numbers || policy.require_numbers, 179 require_special: acc.require_special || policy.require_special, 180 enforce_on_login: acc.enforce_on_login || policy.enforce_on_login, 181 } 182 })); 183 mpp_json["object"] = json!("masterPasswordPolicy"); 184 mpp_json 185 }; 186 let mut result = json!({ 187 "access_token": access_token, 188 "expires_in": expires_in, 189 "token_type": "Bearer", 190 "refresh_token": device.refresh_token, 191 "Key": user.akey, 192 "PrivateKey": user.private_key, 193 "Kdf": kdf, 194 "KdfIterations": kdf_iter, 195 "KdfMemory": kdf_mem, 196 "KdfParallelism": kdf_par, 197 "ResetMasterPassword": false, // TODO: Same as above 198 "ForcePasswordReset": false, 199 "MasterPasswordPolicy": master_password_policy, 200 "scope": scope, 201 "UserDecryptionOptions": { 202 "HasMasterPassword": !user.password_hash.is_empty(), 203 "Object": "userDecryptionOptions" 204 }, 205 }); 206 if let Some(token) = twofactor_token { 207 result["TwoFactorToken"] = Value::String(token); 208 } 209 info!("User {} logged in successfully. IP: {}", username, ip.ip); 210 Ok(Json(result)) 211 } 212 213 /// Retrieves an existing device or creates a new device from ConnectData and the User 214 async fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool) { 215 // On iOS, device_type sends "iOS", on others it sends a number 216 // When unknown or unable to parse, return 14, which is 'Unknown Browser' 217 let device_type = util::try_parse_string(data.device_type.as_ref()).unwrap_or(14i32); 218 let device_id = data 219 .device_identifier 220 .clone() 221 .expect("No device id provided"); 222 let device_name = data.device_name.clone().expect("No device name provided"); 223 let mut new_device = false; 224 // Find device or create new 225 let device = (Device::find_by_uuid_and_user(&device_id, &user.uuid, conn).await).map_or_else( 226 || { 227 new_device = true; 228 Device::new(device_id, user.uuid.clone(), device_name, device_type) 229 }, 230 |device| device, 231 ); 232 (device, new_device) 233 } 234 235 async fn twofactor_auth( 236 user_uuid: &str, 237 data: &ConnectData, 238 device: &mut Device, 239 ip: &ClientIp, 240 conn: &DbConn, 241 ) -> ApiResult<Option<String>> { 242 let (authn, totp_token) = TwoFactorType::get_factors(user_uuid, conn).await?; 243 if authn || totp_token.is_some() { 244 let Some(ref token) = data.two_factor_token else { 245 err_json!( 246 _json_err_twofactor(authn, totp_token.is_some(), user_uuid, conn).await?, 247 "2FA token not provided" 248 ) 249 }; 250 // If no provider is given, we use a fallback. 251 // The fallback is prioritized to be WebAuthn if possible. 252 let tf_type = data 253 .two_factor_provider 254 .map(|prov| { 255 TwoFactorType::try_from(prov) 256 .map_err(Error::from) 257 .and_then(|tf| { 258 if matches!(tf, TwoFactorType::WebAuthn) { 259 if authn { 260 Ok(tf) 261 } else { 262 const MSG: &str = "no webauthn registrations"; 263 Err(Error::new(MSG, MSG)) 264 } 265 } else if totp_token.is_some() { 266 Ok(tf) 267 } else { 268 const MSG: &str = "no totp registrations"; 269 Err(Error::new(MSG, MSG)) 270 } 271 }) 272 }) 273 .transpose()? 274 .unwrap_or({ 275 if authn { 276 TwoFactorType::WebAuthn 277 } else { 278 TwoFactorType::Totp 279 } 280 }); 281 use crate::api::core::two_factor as _tf; 282 match tf_type { 283 TwoFactorType::Totp => { 284 _tf::authenticator::validate_totp_code_str( 285 user_uuid, 286 token, 287 totp_token.unwrap_or_else(|| { 288 unreachable!("no totp registrations, but we verified there are") 289 }), 290 ip, 291 conn, 292 ) 293 .await?; 294 } 295 TwoFactorType::WebAuthn => { 296 _tf::webauthn::validate_webauthn_login(user_uuid, token, conn).await?; 297 } 298 } 299 device.delete_twofactor_remember(); 300 } 301 Ok(None) 302 } 303 304 async fn _json_err_twofactor( 305 authn: bool, 306 totp: bool, 307 user_uuid: &str, 308 conn: &DbConn, 309 ) -> ApiResult<Value> { 310 use crate::api::core::two_factor; 311 let auth_num = i32::from(TwoFactorType::WebAuthn).to_string(); 312 let totp_num = i32::from(TwoFactorType::Totp).to_string(); 313 let providers = [auth_num.as_str(), totp_num.as_str()]; 314 let mut result = json!({ 315 "error" : "invalid_grant", 316 "error_description" : "Two factor required.", 317 "TwoFactorProviders" : if authn { if totp { providers.as_slice() } else { &providers[..1] } } else if totp { &providers[1..] } else { [].as_slice() }, 318 "TwoFactorProviders2" : {}, 319 "MasterPasswordPolicy": { 320 "Object": "masterPasswordPolicy" 321 } 322 }); 323 if authn { 324 let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn).await?; 325 result["TwoFactorProviders2"][auth_num] = request.0; 326 } 327 if totp { 328 result["TwoFactorProviders2"][totp_num] = Value::Null; 329 } 330 Ok(result) 331 } 332 333 #[post("/accounts/prelogin", data = "<data>")] 334 async fn prelogin(data: Json<PreloginData>, conn: DbConn) -> Json<Value> { 335 _prelogin(data, conn).await 336 } 337 338 #[allow(unused_variables, clippy::needless_pass_by_value)] 339 #[post("/accounts/register", data = "<data>")] 340 fn identity_register(data: Json<RegisterData>) -> Error { 341 const MSG: &str = "No more registerations allowed."; 342 Error::new(MSG, MSG) 343 } 344 345 // https://github.com/bitwarden/jslib/blob/master/common/src/models/request/tokenRequest.ts 346 // https://github.com/bitwarden/mobile/blob/master/src/Core/Models/Request/TokenRequest.cs 347 #[derive(Clone, Default, FromForm)] 348 struct ConnectData { 349 #[field(name = uncased("grant_type"))] 350 #[field(name = uncased("granttype"))] 351 grant_type: String, // refresh_token, password, client_credentials (API key) 352 // Needed for grant_type="refresh_token" 353 #[field(name = uncased("refresh_token"))] 354 #[field(name = uncased("refreshtoken"))] 355 refresh_token: Option<String>, 356 // Needed for grant_type = "password" | "client_credentials" 357 #[field(name = uncased("client_id"))] 358 #[field(name = uncased("clientid"))] 359 client_id: Option<String>, // web, cli, desktop, browser, mobile 360 #[field(name = uncased("client_secret"))] 361 #[field(name = uncased("clientsecret"))] 362 #[allow(dead_code)] 363 client_secret: Option<String>, 364 #[field(name = uncased("password"))] 365 password: Option<String>, 366 #[field(name = uncased("scope"))] 367 scope: Option<String>, 368 #[field(name = uncased("username"))] 369 username: Option<String>, 370 #[field(name = uncased("device_identifier"))] 371 #[field(name = uncased("deviceidentifier"))] 372 device_identifier: Option<String>, 373 #[field(name = uncased("device_name"))] 374 #[field(name = uncased("devicename"))] 375 device_name: Option<String>, 376 #[field(name = uncased("device_type"))] 377 #[field(name = uncased("devicetype"))] 378 device_type: Option<String>, 379 #[field(name = uncased("device_push_token"))] 380 #[field(name = uncased("devicepushtoken"))] 381 _device_push_token: Option<String>, // Unused; mobile device push not yet supported. 382 // Needed for two-factor auth 383 #[field(name = uncased("two_factor_provider"))] 384 #[field(name = uncased("twofactorprovider"))] 385 two_factor_provider: Option<i32>, 386 #[field(name = uncased("two_factor_token"))] 387 #[field(name = uncased("twofactortoken"))] 388 two_factor_token: Option<String>, 389 #[field(name = uncased("two_factor_remember"))] 390 #[field(name = uncased("twofactorremember"))] 391 #[allow(dead_code)] 392 two_factor_remember: Option<u32>, 393 #[field(name = uncased("authrequest"))] 394 auth_request: Option<String>, 395 } 396 397 fn _check_is_some<T>(value: &Option<T>, msg: &str) -> EmptyResult { 398 if value.is_none() { 399 err!(msg) 400 } 401 Ok(()) 402 }