diff --git a/src/api/admin.rs b/src/api/admin.rs
index ae30425388..a10df8918a 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -13,7 +13,10 @@ use rocket::{
 };
 
 use crate::{
-    api::{core::log_event, unregister_push_device, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString},
+    api::{
+        core::{log_event, two_factor},
+        unregister_push_device, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString,
+    },
     auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
     config::ConfigBuilder,
     db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
@@ -390,7 +393,7 @@ async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRe
             EventType::OrganizationUserRemoved as i32,
             &user_org.uuid,
             &user_org.org_uuid,
-            String::from(ACTING_ADMIN_USER),
+            ACTING_ADMIN_USER,
             14, // Use UnknownBrowser type
             &token.ip.ip,
             &mut conn,
@@ -445,9 +448,10 @@ async fn enable_user(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyR
 }
 
 #[post("/users/<uuid>/remove-2fa")]
-async fn remove_2fa(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
+async fn remove_2fa(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
     let mut user = get_user_or_404(uuid, &mut conn).await?;
     TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
+    two_factor::enforce_2fa_policy(&user, ACTING_ADMIN_USER, 14, &token.ip.ip, &mut conn).await?;
     user.totp_recover = None;
     user.save(&mut conn).await
 }
@@ -517,7 +521,7 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu
         EventType::OrganizationUserUpdated as i32,
         &user_to_edit.uuid,
         &data.org_uuid,
-        String::from(ACTING_ADMIN_USER),
+        ACTING_ADMIN_USER,
         14, // Use UnknownBrowser type
         &token.ip.ip,
         &mut conn,
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
index 0410d68ed4..a4a2d8365b 100644
--- a/src/api/core/ciphers.rs
+++ b/src/api/core/ciphers.rs
@@ -510,7 +510,7 @@ pub async fn update_cipher_from_data(
                 event_type as i32,
                 &cipher.uuid,
                 org_uuid,
-                headers.user.uuid.clone(),
+                &headers.user.uuid,
                 headers.device.atype,
                 &headers.ip.ip,
                 conn,
@@ -791,7 +791,7 @@ async fn post_collections_admin(
         EventType::CipherUpdatedCollections as i32,
         &cipher.uuid,
         &cipher.organization_uuid.unwrap(),
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -1145,7 +1145,7 @@ async fn save_attachment(
             EventType::CipherAttachmentCreated as i32,
             &cipher.uuid,
             org_uuid,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             &mut conn,
@@ -1479,7 +1479,7 @@ async fn delete_all(
                             EventType::OrganizationPurgedVault as i32,
                             &org_data.org_id,
                             &org_data.org_id,
-                            user.uuid,
+                            &user.uuid,
                             headers.device.atype,
                             &headers.ip.ip,
                             &mut conn,
@@ -1560,16 +1560,8 @@ async fn _delete_cipher_by_uuid(
             false => EventType::CipherDeleted as i32,
         };
 
-        log_event(
-            event_type,
-            &cipher.uuid,
-            &org_uuid,
-            headers.user.uuid.clone(),
-            headers.device.atype,
-            &headers.ip.ip,
-            conn,
-        )
-        .await;
+        log_event(event_type, &cipher.uuid, &org_uuid, &headers.user.uuid, headers.device.atype, &headers.ip.ip, conn)
+            .await;
     }
 
     Ok(())
@@ -1629,7 +1621,7 @@ async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbCon
             EventType::CipherRestored as i32,
             &cipher.uuid.clone(),
             org_uuid,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             conn,
@@ -1713,7 +1705,7 @@ async fn _delete_cipher_attachment_by_id(
             EventType::CipherAttachmentDeleted as i32,
             &cipher.uuid,
             &org_uuid,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             conn,
diff --git a/src/api/core/events.rs b/src/api/core/events.rs
index 70704d792d..9a2027e0e9 100644
--- a/src/api/core/events.rs
+++ b/src/api/core/events.rs
@@ -263,7 +263,7 @@ pub async fn log_event(
     event_type: i32,
     source_uuid: &str,
     org_uuid: &str,
-    act_user_uuid: String,
+    act_user_uuid: &str,
     device_type: i32,
     ip: &IpAddr,
     conn: &mut DbConn,
@@ -271,7 +271,7 @@ pub async fn log_event(
     if !CONFIG.org_events_enabled() {
         return;
     }
-    _log_event(event_type, source_uuid, org_uuid, &act_user_uuid, device_type, None, ip, conn).await;
+    _log_event(event_type, source_uuid, org_uuid, act_user_uuid, device_type, None, ip, conn).await;
 }
 
 #[allow(clippy::too_many_arguments)]
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
index 59079e0121..f57d85061c 100644
--- a/src/api/core/organizations.rs
+++ b/src/api/core/organizations.rs
@@ -5,7 +5,7 @@ use serde_json::Value;
 
 use crate::{
     api::{
-        core::{log_event, CipherSyncData, CipherSyncType},
+        core::{log_event, two_factor, CipherSyncData, CipherSyncType},
         EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordOrOtpData,
         UpdateType,
     },
@@ -226,7 +226,7 @@ async fn leave_organization(org_id: &str, headers: Headers, mut conn: DbConn) ->
                 EventType::OrganizationUserRemoved as i32,
                 &user_org.uuid,
                 org_id,
-                headers.user.uuid.clone(),
+                &headers.user.uuid,
                 headers.device.atype,
                 &headers.ip.ip,
                 &mut conn,
@@ -279,7 +279,7 @@ async fn post_organization(
         EventType::OrganizationUpdated as i32,
         org_id,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -396,7 +396,7 @@ async fn post_organization_collections(
         EventType::CollectionCreated as i32,
         &collection.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -477,7 +477,7 @@ async fn post_organization_collection_update(
         EventType::CollectionUpdated as i32,
         &collection.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -565,7 +565,7 @@ async fn _delete_organization_collection(
                     EventType::CollectionDeleted as i32,
                     &collection.uuid,
                     org_id,
-                    headers.user.uuid.clone(),
+                    &headers.user.uuid,
                     headers.device.atype,
                     &headers.ip.ip,
                     conn,
@@ -946,7 +946,7 @@ async fn send_invite(
             EventType::OrganizationUserInvited as i32,
             &new_user.uuid,
             org_id,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             &mut conn,
@@ -1240,7 +1240,7 @@ async fn _confirm_invite(
         EventType::OrganizationUserConfirmed as i32,
         &user_to_confirm.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         conn,
@@ -1402,7 +1402,7 @@ async fn edit_user(
         EventType::OrganizationUserUpdated as i32,
         &user_to_edit.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -1494,7 +1494,7 @@ async fn _delete_user(
         EventType::OrganizationUserRemoved as i32,
         &user_to_delete.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         conn,
@@ -1697,38 +1697,16 @@ async fn put_policy(
         None => err!("Invalid or unsupported policy type"),
     };
 
-    // When enabling the TwoFactorAuthentication policy, remove this org's members that do have 2FA
+    // When enabling the TwoFactorAuthentication policy, revoke all members that do not have 2FA
     if pol_type_enum == OrgPolicyType::TwoFactorAuthentication && data.enabled {
-        for member in UserOrganization::find_by_org(org_id, &mut conn).await.into_iter() {
-            let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &mut conn).await.is_empty();
-
-            // Policy only applies to non-Owner/non-Admin members who have accepted joining the org
-            // Invited users still need to accept the invite and will get an error when they try to accept the invite.
-            if user_twofactor_disabled
-                && member.atype < UserOrgType::Admin
-                && member.status != UserOrgStatus::Invited as i32
-            {
-                if CONFIG.mail_enabled() {
-                    let org = Organization::find_by_uuid(&member.org_uuid, &mut conn).await.unwrap();
-                    let user = User::find_by_uuid(&member.user_uuid, &mut conn).await.unwrap();
-
-                    mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
-                }
-
-                log_event(
-                    EventType::OrganizationUserRemoved as i32,
-                    &member.uuid,
-                    org_id,
-                    headers.user.uuid.clone(),
-                    headers.device.atype,
-                    &headers.ip.ip,
-                    &mut conn,
-                )
-                .await;
-
-                member.delete(&mut conn).await?;
-            }
-        }
+        two_factor::enforce_2fa_policy_for_org(
+            org_id,
+            &headers.user.uuid,
+            headers.device.atype,
+            &headers.ip.ip,
+            &mut conn,
+        )
+        .await?;
     }
 
     // When enabling the SingleOrg policy, remove this org's members that are members of other orgs
@@ -1753,7 +1731,7 @@ async fn put_policy(
                     EventType::OrganizationUserRemoved as i32,
                     &member.uuid,
                     org_id,
-                    headers.user.uuid.clone(),
+                    &headers.user.uuid,
                     headers.device.atype,
                     &headers.ip.ip,
                     &mut conn,
@@ -1778,7 +1756,7 @@ async fn put_policy(
         EventType::PolicyUpdated as i32,
         &policy.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -1895,7 +1873,7 @@ async fn import(org_id: &str, data: JsonUpcase<OrgImportData>, headers: Headers,
                     EventType::OrganizationUserRemoved as i32,
                     &user_org.uuid,
                     org_id,
-                    headers.user.uuid.clone(),
+                    &headers.user.uuid,
                     headers.device.atype,
                     &headers.ip.ip,
                     &mut conn,
@@ -1925,7 +1903,7 @@ async fn import(org_id: &str, data: JsonUpcase<OrgImportData>, headers: Headers,
                     EventType::OrganizationUserInvited as i32,
                     &new_org_user.uuid,
                     org_id,
-                    headers.user.uuid.clone(),
+                    &headers.user.uuid,
                     headers.device.atype,
                     &headers.ip.ip,
                     &mut conn,
@@ -1961,7 +1939,7 @@ async fn import(org_id: &str, data: JsonUpcase<OrgImportData>, headers: Headers,
                         EventType::OrganizationUserRemoved as i32,
                         &user_org.uuid,
                         org_id,
-                        headers.user.uuid.clone(),
+                        &headers.user.uuid,
                         headers.device.atype,
                         &headers.ip.ip,
                         &mut conn,
@@ -2074,7 +2052,7 @@ async fn _revoke_organization_user(
                 EventType::OrganizationUserRevoked as i32,
                 &user_org.uuid,
                 org_id,
-                headers.user.uuid.clone(),
+                &headers.user.uuid,
                 headers.device.atype,
                 &headers.ip.ip,
                 conn,
@@ -2193,7 +2171,7 @@ async fn _restore_organization_user(
                 EventType::OrganizationUserRestored as i32,
                 &user_org.uuid,
                 org_id,
-                headers.user.uuid.clone(),
+                &headers.user.uuid,
                 headers.device.atype,
                 &headers.ip.ip,
                 conn,
@@ -2322,7 +2300,7 @@ async fn post_groups(
         EventType::GroupCreated as i32,
         &group.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -2359,7 +2337,7 @@ async fn put_group(
         EventType::GroupUpdated as i32,
         &updated_group.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -2392,7 +2370,7 @@ async fn add_update_group(
             EventType::OrganizationUserUpdatedGroups as i32,
             &assigned_user_id,
             org_id,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             conn,
@@ -2447,7 +2425,7 @@ async fn _delete_group(org_id: &str, group_id: &str, headers: &AdminHeaders, con
         EventType::GroupDeleted as i32,
         &group.uuid,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         conn,
@@ -2538,7 +2516,7 @@ async fn put_group_users(
             EventType::OrganizationUserUpdatedGroups as i32,
             &assigned_user_id,
             org_id,
-            headers.user.uuid.clone(),
+            &headers.user.uuid,
             headers.device.atype,
             &headers.ip.ip,
             &mut conn,
@@ -2616,7 +2594,7 @@ async fn put_user_groups(
         EventType::OrganizationUserUpdatedGroups as i32,
         org_user_id,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -2671,7 +2649,7 @@ async fn delete_group_user(
         EventType::OrganizationUserUpdatedGroups as i32,
         org_user_id,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -2760,7 +2738,7 @@ async fn put_reset_password(
         EventType::OrganizationUserAdminResetPassword as i32,
         org_user_id,
         org_id,
-        headers.user.uuid.clone(),
+        &headers.user.uuid,
         headers.device.atype,
         &headers.ip.ip,
         &mut conn,
@@ -2887,8 +2865,7 @@ async fn put_reset_password_enrollment(
         EventType::OrganizationUserResetPasswordWithdraw as i32
     };
 
-    log_event(log_id, org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &mut conn)
-        .await;
+    log_event(log_id, org_user_id, org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
 
     Ok(())
 }
diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs
index 41368666de..9a922d26f6 100644
--- a/src/api/core/two_factor/mod.rs
+++ b/src/api/core/two_factor/mod.rs
@@ -5,7 +5,10 @@ use rocket::Route;
 use serde_json::Value;
 
 use crate::{
-    api::{core::log_user_event, JsonResult, JsonUpcase, NumberOrString, PasswordOrOtpData},
+    api::{
+        core::{log_event, log_user_event},
+        EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordOrOtpData,
+    },
     auth::{ClientHeaders, Headers},
     crypto,
     db::{models::*, DbConn, DbPool},
@@ -96,6 +99,7 @@ async fn recover(data: JsonUpcase<RecoverTwoFactor>, client_headers: ClientHeade
 
     // Remove all twofactors from the user
     TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
+    enforce_2fa_policy(&user, &user.uuid, client_headers.device_type, &client_headers.ip.ip, &mut conn).await?;
 
     log_user_event(
         EventType::UserRecovered2fa as i32,
@@ -149,22 +153,8 @@ async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Head
             .await;
     }
 
-    let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty();
-
-    if twofactor_disabled {
-        for user_org in
-            UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &mut conn)
-                .await
-                .into_iter()
-        {
-            if user_org.atype < UserOrgType::Admin {
-                if CONFIG.mail_enabled() {
-                    let org = Organization::find_by_uuid(&user_org.org_uuid, &mut conn).await.unwrap();
-                    mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
-                }
-                user_org.delete(&mut conn).await?;
-            }
-        }
+    if TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty() {
+        enforce_2fa_policy(&user, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await?;
     }
 
     Ok(Json(json!({
@@ -179,6 +169,78 @@ async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers:
     disable_twofactor(data, headers, conn).await
 }
 
+pub async fn enforce_2fa_policy(
+    user: &User,
+    act_uuid: &str,
+    device_type: i32,
+    ip: &std::net::IpAddr,
+    conn: &mut DbConn,
+) -> EmptyResult {
+    for member in UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn)
+        .await
+        .into_iter()
+    {
+        // Policy only applies to non-Owner/non-Admin members who have accepted joining the org
+        if member.atype < UserOrgType::Admin {
+            if CONFIG.mail_enabled() {
+                let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap();
+                mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
+            }
+            let mut member = member;
+            member.revoke();
+            member.save(conn).await?;
+
+            log_event(
+                EventType::OrganizationUserRevoked as i32,
+                &member.uuid,
+                &member.org_uuid,
+                act_uuid,
+                device_type,
+                ip,
+                conn,
+            )
+            .await;
+        }
+    }
+
+    Ok(())
+}
+
+pub async fn enforce_2fa_policy_for_org(
+    org_uuid: &str,
+    act_uuid: &str,
+    device_type: i32,
+    ip: &std::net::IpAddr,
+    conn: &mut DbConn,
+) -> EmptyResult {
+    let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap();
+    for member in UserOrganization::find_confirmed_by_org(org_uuid, conn).await.into_iter() {
+        // Don't enforce the policy for Admins and Owners.
+        if member.atype < UserOrgType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() {
+            if CONFIG.mail_enabled() {
+                let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap();
+                mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
+            }
+            let mut member = member;
+            member.revoke();
+            member.save(conn).await?;
+
+            log_event(
+                EventType::OrganizationUserRevoked as i32,
+                &member.uuid,
+                org_uuid,
+                act_uuid,
+                device_type,
+                ip,
+                conn,
+            )
+            .await;
+        }
+    }
+
+    Ok(())
+}
+
 pub async fn send_incomplete_2fa_notifications(pool: DbPool) {
     debug!("Sending notifications for incomplete 2FA logins");
 
diff --git a/src/api/identity.rs b/src/api/identity.rs
index 13e7ab433f..12becf6b1c 100644
--- a/src/api/identity.rs
+++ b/src/api/identity.rs
@@ -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),
                 })
diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs
index 620d7428e1..144ea56741 100644
--- a/src/db/models/organization.rs
+++ b/src/db/models/organization.rs
@@ -665,6 +665,16 @@ impl UserOrganization {
         }}
     }
 
+    pub async fn find_confirmed_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
+        db_run! { conn: {
+            users_organizations::table
+                .filter(users_organizations::org_uuid.eq(org_uuid))
+                .filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
+                .load::<UserOrganizationDb>(conn)
+                .unwrap_or_default().from_db()
+        }}
+    }
+
     pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
         db_run! { conn: {
             users_organizations::table