mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 13:51:14 +00:00 
			
		
		
		
	web-client now request email 2fa (#5871)
This commit is contained in:
		
					parent
					
						
							
								602b18fdd6
							
						
					
				
			
			
				commit
				
					
						a039e227c7
					
				
			
		
					 1 changed files with 27 additions and 8 deletions
				
			
		|  | @ -17,7 +17,7 @@ use crate::{ | ||||||
|         push::register_push_device, |         push::register_push_device, | ||||||
|         ApiResult, EmptyResult, JsonResult, |         ApiResult, EmptyResult, JsonResult, | ||||||
|     }, |     }, | ||||||
|     auth::{generate_organization_api_key_login_claims, ClientHeaders, ClientIp}, |     auth::{generate_organization_api_key_login_claims, ClientHeaders, ClientIp, ClientVersion}, | ||||||
|     db::{models::*, DbConn}, |     db::{models::*, DbConn}, | ||||||
|     error::MapResult, |     error::MapResult, | ||||||
|     mail, util, CONFIG, |     mail, util, CONFIG, | ||||||
|  | @ -28,7 +28,12 @@ pub fn routes() -> Vec<Route> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[post("/connect/token", data = "<data>")] | #[post("/connect/token", data = "<data>")] | ||||||
| async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult { | async fn login( | ||||||
|  |     data: Form<ConnectData>, | ||||||
|  |     client_header: ClientHeaders, | ||||||
|  |     client_version: Option<ClientVersion>, | ||||||
|  |     mut conn: DbConn, | ||||||
|  | ) -> JsonResult { | ||||||
|     let data: ConnectData = data.into_inner(); |     let data: ConnectData = data.into_inner(); | ||||||
| 
 | 
 | ||||||
|     let mut user_id: Option<UserId> = None; |     let mut user_id: Option<UserId> = None; | ||||||
|  | @ -48,7 +53,7 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: | ||||||
|             _check_is_some(&data.device_name, "device_name cannot be blank")?; |             _check_is_some(&data.device_name, "device_name cannot be blank")?; | ||||||
|             _check_is_some(&data.device_type, "device_type cannot be blank")?; |             _check_is_some(&data.device_type, "device_type cannot be blank")?; | ||||||
| 
 | 
 | ||||||
|             _password_login(data, &mut user_id, &mut conn, &client_header.ip).await |             _password_login(data, &mut user_id, &mut conn, &client_header.ip, &client_version).await | ||||||
|         } |         } | ||||||
|         "client_credentials" => { |         "client_credentials" => { | ||||||
|             _check_is_some(&data.client_id, "client_id cannot be blank")?; |             _check_is_some(&data.client_id, "client_id cannot be blank")?; | ||||||
|  | @ -144,6 +149,7 @@ async fn _password_login( | ||||||
|     user_id: &mut Option<UserId>, |     user_id: &mut Option<UserId>, | ||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
|     ip: &ClientIp, |     ip: &ClientIp, | ||||||
|  |     client_version: &Option<ClientVersion>, | ||||||
| ) -> JsonResult { | ) -> JsonResult { | ||||||
|     // Validate scope
 |     // Validate scope
 | ||||||
|     let scope = data.scope.as_ref().unwrap(); |     let scope = data.scope.as_ref().unwrap(); | ||||||
|  | @ -262,7 +268,7 @@ async fn _password_login( | ||||||
| 
 | 
 | ||||||
|     let (mut device, new_device) = get_device(&data, conn, &user).await; |     let (mut device, new_device) = get_device(&data, conn, &user).await; | ||||||
| 
 | 
 | ||||||
|     let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, conn).await?; |     let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, client_version, conn).await?; | ||||||
| 
 | 
 | ||||||
|     if CONFIG.mail_enabled() && new_device { |     if CONFIG.mail_enabled() && new_device { | ||||||
|         if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await { |         if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await { | ||||||
|  | @ -520,6 +526,7 @@ async fn twofactor_auth( | ||||||
|     data: &ConnectData, |     data: &ConnectData, | ||||||
|     device: &mut Device, |     device: &mut Device, | ||||||
|     ip: &ClientIp, |     ip: &ClientIp, | ||||||
|  |     client_version: &Option<ClientVersion>, | ||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
| ) -> ApiResult<Option<String>> { | ) -> ApiResult<Option<String>> { | ||||||
|     let twofactors = TwoFactor::find_by_user(&user.uuid, conn).await; |     let twofactors = TwoFactor::find_by_user(&user.uuid, conn).await; | ||||||
|  | @ -538,7 +545,10 @@ async fn twofactor_auth( | ||||||
|     let twofactor_code = match data.two_factor_token { |     let twofactor_code = match data.two_factor_token { | ||||||
|         Some(ref code) => code, |         Some(ref code) => code, | ||||||
|         None => { |         None => { | ||||||
|             err_json!(_json_err_twofactor(&twofactor_ids, &user.uuid, data, conn).await?, "2FA token not provided") |             err_json!( | ||||||
|  |                 _json_err_twofactor(&twofactor_ids, &user.uuid, data, client_version, conn).await?, | ||||||
|  |                 "2FA token not provided" | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -585,7 +595,7 @@ async fn twofactor_auth( | ||||||
|                 } |                 } | ||||||
|                 _ => { |                 _ => { | ||||||
|                     err_json!( |                     err_json!( | ||||||
|                         _json_err_twofactor(&twofactor_ids, &user.uuid, data, conn).await?, |                         _json_err_twofactor(&twofactor_ids, &user.uuid, data, client_version, conn).await?, | ||||||
|                         "2FA Remember token not provided" |                         "2FA Remember token not provided" | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|  | @ -617,6 +627,7 @@ async fn _json_err_twofactor( | ||||||
|     providers: &[i32], |     providers: &[i32], | ||||||
|     user_id: &UserId, |     user_id: &UserId, | ||||||
|     data: &ConnectData, |     data: &ConnectData, | ||||||
|  |     client_version: &Option<ClientVersion>, | ||||||
|     conn: &mut DbConn, |     conn: &mut DbConn, | ||||||
| ) -> ApiResult<Value> { | ) -> ApiResult<Value> { | ||||||
|     let mut result = json!({ |     let mut result = json!({ | ||||||
|  | @ -689,8 +700,16 @@ async fn _json_err_twofactor( | ||||||
|                     err!("No twofactor email registered") |                     err!("No twofactor email registered") | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 // Send email immediately if email is the only 2FA option
 |                 // Starting with version 2025.5.0 the client will call `/api/two-factor/send-email-login`.
 | ||||||
|                 if providers.len() == 1 { |                 let disabled_send = if let Some(cv) = client_version { | ||||||
|  |                     let ver_match = semver::VersionReq::parse(">=2025.5.0").unwrap(); | ||||||
|  |                     ver_match.matches(&cv.0) | ||||||
|  |                 } else { | ||||||
|  |                     false | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 // Send email immediately if email is the only 2FA option.
 | ||||||
|  |                 if providers.len() == 1 && !disabled_send { | ||||||
|                     email::send_token(user_id, conn).await? |                     email::send_token(user_id, conn).await? | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue