|
|
@ -9,9 +9,11 @@ use serde_json::Value; |
|
|
|
|
|
|
|
use crate::{ |
|
|
|
api::{ |
|
|
|
core::accounts::{PreloginData, RegisterData, _prelogin, _register}, |
|
|
|
core::log_user_event, |
|
|
|
core::two_factor::{duo, email, email::EmailTokenData, yubikey}, |
|
|
|
core::{ |
|
|
|
accounts::{PreloginData, RegisterData, _prelogin, _register}, |
|
|
|
log_user_event, |
|
|
|
two_factor::{authenticator, duo, email, enforce_2fa_policy, webauthn, yubikey}, |
|
|
|
}, |
|
|
|
ApiResult, EmptyResult, JsonResult, JsonUpcase, |
|
|
|
}, |
|
|
|
auth::{generate_organization_api_key_login_claims, ClientHeaders, ClientIp}, |
|
|
@ -247,7 +249,7 @@ async fn _password_login( |
|
|
|
|
|
|
|
let (mut device, new_device) = get_device(&data, conn, &user).await; |
|
|
|
|
|
|
|
let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, conn).await?; |
|
|
|
let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, conn).await?; |
|
|
|
|
|
|
|
if CONFIG.mail_enabled() && new_device { |
|
|
|
if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await { |
|
|
@ -468,32 +470,32 @@ async fn get_device(data: &ConnectData, conn: &mut DbConn, user: &User) -> (Devi |
|
|
|
} |
|
|
|
|
|
|
|
async fn twofactor_auth( |
|
|
|
user_uuid: &str, |
|
|
|
user: &User, |
|
|
|
data: &ConnectData, |
|
|
|
device: &mut Device, |
|
|
|
ip: &ClientIp, |
|
|
|
conn: &mut DbConn, |
|
|
|
) -> ApiResult<Option<String>> { |
|
|
|
let twofactors = TwoFactor::find_by_user(user_uuid, conn).await; |
|
|
|
let twofactors = TwoFactor::find_by_user(&user.uuid, conn).await; |
|
|
|
|
|
|
|
// No twofactor token if twofactor is disabled
|
|
|
|
if twofactors.is_empty() { |
|
|
|
enforce_2fa_policy(user, &user.uuid, device.atype, &ip.ip, conn).await?; |
|
|
|
return Ok(None); |
|
|
|
} |
|
|
|
|
|
|
|
TwoFactorIncomplete::mark_incomplete(user_uuid, &device.uuid, &device.name, ip, conn).await?; |
|
|
|
TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, ip, conn).await?; |
|
|
|
|
|
|
|
let twofactor_ids: Vec<_> = twofactors.iter().map(|tf| tf.atype).collect(); |
|
|
|
let selected_id = data.two_factor_provider.unwrap_or(twofactor_ids[0]); // If we aren't given a two factor provider, assume the first one
|
|
|
|
|
|
|
|
let twofactor_code = match data.two_factor_token { |
|
|
|
Some(ref code) => code, |
|
|
|
None => err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn).await?, "2FA token not provided"), |
|
|
|
None => err_json!(_json_err_twofactor(&twofactor_ids, &user.uuid, conn).await?, "2FA token not provided"), |
|
|
|
}; |
|
|
|
|
|
|
|
let selected_twofactor = twofactors.into_iter().find(|tf| tf.atype == selected_id && tf.enabled); |
|
|
|
|
|
|
|
use crate::api::core::two_factor as _tf; |
|
|
|
use crate::crypto::ct_eq; |
|
|
|
|
|
|
|
let selected_data = _selected_data(selected_twofactor); |
|
|
@ -501,17 +503,15 @@ async fn twofactor_auth( |
|
|
|
|
|
|
|
match TwoFactorType::from_i32(selected_id) { |
|
|
|
Some(TwoFactorType::Authenticator) => { |
|
|
|
_tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn).await? |
|
|
|
} |
|
|
|
Some(TwoFactorType::Webauthn) => { |
|
|
|
_tf::webauthn::validate_webauthn_login(user_uuid, twofactor_code, conn).await? |
|
|
|
authenticator::validate_totp_code_str(&user.uuid, twofactor_code, &selected_data?, ip, conn).await? |
|
|
|
} |
|
|
|
Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?).await?, |
|
|
|
Some(TwoFactorType::Webauthn) => webauthn::validate_webauthn_login(&user.uuid, twofactor_code, conn).await?, |
|
|
|
Some(TwoFactorType::YubiKey) => yubikey::validate_yubikey_login(twofactor_code, &selected_data?).await?, |
|
|
|
Some(TwoFactorType::Duo) => { |
|
|
|
_tf::duo::validate_duo_login(data.username.as_ref().unwrap().trim(), twofactor_code, conn).await? |
|
|
|
duo::validate_duo_login(data.username.as_ref().unwrap().trim(), twofactor_code, conn).await? |
|
|
|
} |
|
|
|
Some(TwoFactorType::Email) => { |
|
|
|
_tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn).await? |
|
|
|
email::validate_email_code_str(&user.uuid, twofactor_code, &selected_data?, conn).await? |
|
|
|
} |
|
|
|
|
|
|
|
Some(TwoFactorType::Remember) => { |
|
|
@ -521,7 +521,7 @@ async fn twofactor_auth( |
|
|
|
} |
|
|
|
_ => { |
|
|
|
err_json!( |
|
|
|
_json_err_twofactor(&twofactor_ids, user_uuid, conn).await?, |
|
|
|
_json_err_twofactor(&twofactor_ids, &user.uuid, conn).await?, |
|
|
|
"2FA Remember token not provided" |
|
|
|
) |
|
|
|
} |
|
|
@ -535,7 +535,7 @@ async fn twofactor_auth( |
|
|
|
), |
|
|
|
} |
|
|
|
|
|
|
|
TwoFactorIncomplete::mark_complete(user_uuid, &device.uuid, conn).await?; |
|
|
|
TwoFactorIncomplete::mark_complete(&user.uuid, &device.uuid, conn).await?; |
|
|
|
|
|
|
|
if !CONFIG.disable_2fa_remember() && remember == 1 { |
|
|
|
Ok(Some(device.refresh_twofactor_remember())) |
|
|
@ -550,8 +550,6 @@ fn _selected_data(tf: Option<TwoFactor>) -> ApiResult<String> { |
|
|
|
} |
|
|
|
|
|
|
|
async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbConn) -> ApiResult<Value> { |
|
|
|
use crate::api::core::two_factor; |
|
|
|
|
|
|
|
let mut result = json!({ |
|
|
|
"error" : "invalid_grant", |
|
|
|
"error_description" : "Two factor required.", |
|
|
@ -566,7 +564,7 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo |
|
|
|
Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } |
|
|
|
|
|
|
|
Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => { |
|
|
|
let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn).await?; |
|
|
|
let request = webauthn::generate_webauthn_login(user_uuid, conn).await?; |
|
|
|
result["TwoFactorProviders2"][provider.to_string()] = request.0; |
|
|
|
} |
|
|
|
|
|
|
@ -598,8 +596,6 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo |
|
|
|
} |
|
|
|
|
|
|
|
Some(tf_type @ TwoFactorType::Email) => { |
|
|
|
use crate::api::core::two_factor as _tf; |
|
|
|
|
|
|
|
let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await { |
|
|
|
Some(tf) => tf, |
|
|
|
None => err!("No twofactor email registered"), |
|
|
@ -607,10 +603,10 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo |
|
|
|
|
|
|
|
// Send email immediately if email is the only 2FA option
|
|
|
|
if providers.len() == 1 { |
|
|
|
_tf::email::send_token(user_uuid, conn).await? |
|
|
|
email::send_token(user_uuid, conn).await? |
|
|
|
} |
|
|
|
|
|
|
|
let email_data = EmailTokenData::from_json(&twofactor.data)?; |
|
|
|
let email_data = email::EmailTokenData::from_json(&twofactor.data)?; |
|
|
|
result["TwoFactorProviders2"][provider.to_string()] = json!({ |
|
|
|
"Email": email::obscure_email(&email_data.email), |
|
|
|
}) |
|
|
|