From 87e08b9e50083d5b4aa3219ae5dcb67213427486 Mon Sep 17 00:00:00 2001
From: BlackDex <black.dex@gmail.com>
Date: Tue, 16 Nov 2021 17:07:55 +0100
Subject: [PATCH] Async/Awaited all db methods

This is a rather large PR which updates the async branch to have all the
database methods as an async fn.

Some iter/map logic needed to be changed to a stream::iter().then(), but
besides that most changes were just adding async/await where needed.
---
 rustfmt.toml                             |   8 +-
 src/api/admin.rs                         | 147 +++----
 src/api/core/accounts.rs                 | 124 +++---
 src/api/core/ciphers.rs                  | 490 +++++++++++++----------
 src/api/core/emergency_access.rs         | 216 +++++-----
 src/api/core/folders.rs                  |  45 ++-
 src/api/core/mod.rs                      |   8 +-
 src/api/core/organizations.rs            | 419 ++++++++++---------
 src/api/core/sends.rs                    |  96 ++---
 src/api/core/two_factor/authenticator.rs |  37 +-
 src/api/core/two_factor/duo.rs           |  31 +-
 src/api/core/two_factor/email.rs         |  54 +--
 src/api/core/two_factor/mod.rs           |  48 +--
 src/api/core/two_factor/u2f.rs           |  61 +--
 src/api/core/two_factor/webauthn.rs      |  66 +--
 src/api/core/two_factor/yubikey.rs       |  23 +-
 src/api/icons.rs                         |   1 +
 src/api/identity.rs                      |  91 +++--
 src/auth.rs                              |  12 +-
 src/db/mod.rs                            |   2 +-
 src/db/models/attachment.rs              |  22 +-
 src/db/models/cipher.rs                  | 123 +++---
 src/db/models/collection.rs              |  98 ++---
 src/db/models/device.rs                  |  18 +-
 src/db/models/emergency_access.rs        |  42 +-
 src/db/models/favorite.rs                |  14 +-
 src/db/models/folder.rs                  |  32 +-
 src/db/models/org_policy.rs              |  34 +-
 src/db/models/organization.rs            |  87 ++--
 src/db/models/send.rs                    |  46 +--
 src/db/models/two_factor.rs              |  19 +-
 src/db/models/two_factor_incomplete.rs   |  20 +-
 src/db/models/user.rs                    |  78 ++--
 src/main.rs                              |   4 +-
 34 files changed, 1421 insertions(+), 1195 deletions(-)

diff --git a/rustfmt.toml b/rustfmt.toml
index 630b42b2..0b46f6cb 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,7 +1,7 @@
-version = "Two"
-edition = "2018"
+#version = "One"
+edition = "2021"
 max_width = 120
 newline_style = "Unix"
 use_small_heuristics = "Off"
-struct_lit_single_line = false
-overflow_delimited_expr = true
+#struct_lit_single_line = false
+#overflow_delimited_expr = true
diff --git a/src/api/admin.rs b/src/api/admin.rs
index c25587d0..7d81ec7b 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -25,6 +25,8 @@ use crate::{
     CONFIG, VERSION,
 };
 
+use futures::{stream, stream::StreamExt};
+
 pub fn routes() -> Vec<Route> {
     if !CONFIG.disable_admin_token() && !CONFIG.is_admin_token_set() {
         return routes![admin_disabled];
@@ -253,8 +255,8 @@ struct InviteData {
     email: String,
 }
 
-fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult<User> {
-    if let Some(user) = User::find_by_uuid(uuid, conn) {
+async fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult<User> {
+    if let Some(user) = User::find_by_uuid(uuid, conn).await {
         Ok(user)
     } else {
         err_code!("User doesn't exist", Status::NotFound.code);
@@ -262,30 +264,28 @@ fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult<User> {
 }
 
 #[post("/invite", data = "<data>")]
-fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> JsonResult {
+async fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> JsonResult {
     let data: InviteData = data.into_inner();
     let email = data.email.clone();
-    if User::find_by_mail(&data.email, &conn).is_some() {
+    if User::find_by_mail(&data.email, &conn).await.is_some() {
         err_code!("User already exists", Status::Conflict.code)
     }
 
     let mut user = User::new(email);
 
-    // TODO: After try_blocks is stabilized, this can be made more readable
-    // See: https://github.com/rust-lang/rust/issues/31436
-    (|| {
+    async fn _generate_invite(user: &User, conn: &DbConn) -> EmptyResult {
         if CONFIG.mail_enabled() {
-            mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None)?;
+            mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None)
         } else {
             let invitation = Invitation::new(user.email.clone());
-            invitation.save(&conn)?;
+            invitation.save(conn).await
         }
+    }
 
-        user.save(&conn)
-    })()
-    .map_err(|e| e.with_code(Status::InternalServerError.code))?;
+    _generate_invite(&user, &conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?;
+    user.save(&conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?;
 
-    Ok(Json(user.to_json(&conn)))
+    Ok(Json(user.to_json(&conn).await))
 }
 
 #[post("/test/smtp", data = "<data>")]
@@ -306,84 +306,90 @@ fn logout(cookies: &CookieJar, referer: Referer) -> Redirect {
 }
 
 #[get("/users")]
-fn get_users_json(_token: AdminToken, conn: DbConn) -> Json<Value> {
-    let users = User::get_all(&conn);
-    let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
+async fn get_users_json(_token: AdminToken, conn: DbConn) -> Json<Value> {
+    let users_json = stream::iter(User::get_all(&conn).await)
+        .then(|u| async {
+            let u = u; // Move out this single variable
+            u.to_json(&conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     Json(Value::Array(users_json))
 }
 
 #[get("/users/overview")]
-fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
-    let users = User::get_all(&conn);
-    let dt_fmt = "%Y-%m-%d %H:%M:%S %Z";
-    let users_json: Vec<Value> = users
-        .iter()
-        .map(|u| {
-            let mut usr = u.to_json(&conn);
-            usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &conn));
-            usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &conn));
-            usr["attachment_size"] = json!(get_display_size(Attachment::size_by_user(&u.uuid, &conn) as i32));
+async fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
+    const DT_FMT: &str = "%Y-%m-%d %H:%M:%S %Z";
+
+    let users_json = stream::iter(User::get_all(&conn).await)
+        .then(|u| async {
+            let u = u; // Move out this single variable
+            let mut usr = u.to_json(&conn).await;
+            usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &conn).await);
+            usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &conn).await);
+            usr["attachment_size"] = json!(get_display_size(Attachment::size_by_user(&u.uuid, &conn).await as i32));
             usr["user_enabled"] = json!(u.enabled);
-            usr["created_at"] = json!(format_naive_datetime_local(&u.created_at, dt_fmt));
-            usr["last_active"] = match u.last_active(&conn) {
-                Some(dt) => json!(format_naive_datetime_local(&dt, dt_fmt)),
+            usr["created_at"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT));
+            usr["last_active"] = match u.last_active(&conn).await {
+                Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)),
                 None => json!("Never"),
             };
             usr
         })
-        .collect();
+        .collect::<Vec<Value>>()
+        .await;
 
     let text = AdminTemplateData::with_data("admin/users", json!(users_json)).render()?;
     Ok(Html(text))
 }
 
 #[get("/users/<uuid>")]
-fn get_user_json(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult {
-    let user = get_user_or_404(&uuid, &conn)?;
+async fn get_user_json(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult {
+    let user = get_user_or_404(&uuid, &conn).await?;
 
-    Ok(Json(user.to_json(&conn)))
+    Ok(Json(user.to_json(&conn).await))
 }
 
 #[post("/users/<uuid>/delete")]
-fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let user = get_user_or_404(&uuid, &conn)?;
-    user.delete(&conn)
+async fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let user = get_user_or_404(&uuid, &conn).await?;
+    user.delete(&conn).await
 }
 
 #[post("/users/<uuid>/deauth")]
-fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let mut user = get_user_or_404(&uuid, &conn)?;
-    Device::delete_all_by_user(&user.uuid, &conn)?;
+async fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let mut user = get_user_or_404(&uuid, &conn).await?;
+    Device::delete_all_by_user(&user.uuid, &conn).await?;
     user.reset_security_stamp();
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[post("/users/<uuid>/disable")]
-fn disable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let mut user = get_user_or_404(&uuid, &conn)?;
-    Device::delete_all_by_user(&user.uuid, &conn)?;
+async fn disable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let mut user = get_user_or_404(&uuid, &conn).await?;
+    Device::delete_all_by_user(&user.uuid, &conn).await?;
     user.reset_security_stamp();
     user.enabled = false;
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[post("/users/<uuid>/enable")]
-fn enable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let mut user = get_user_or_404(&uuid, &conn)?;
+async fn enable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let mut user = get_user_or_404(&uuid, &conn).await?;
     user.enabled = true;
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[post("/users/<uuid>/remove-2fa")]
-fn remove_2fa(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let mut user = get_user_or_404(&uuid, &conn)?;
-    TwoFactor::delete_all_by_user(&user.uuid, &conn)?;
+async fn remove_2fa(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let mut user = get_user_or_404(&uuid, &conn).await?;
+    TwoFactor::delete_all_by_user(&user.uuid, &conn).await?;
     user.totp_recover = None;
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[derive(Deserialize, Debug)]
@@ -394,10 +400,10 @@ struct UserOrgTypeData {
 }
 
 #[post("/users/org_type", data = "<data>")]
-fn update_user_org_type(data: Json<UserOrgTypeData>, _token: AdminToken, conn: DbConn) -> EmptyResult {
+async fn update_user_org_type(data: Json<UserOrgTypeData>, _token: AdminToken, conn: DbConn) -> EmptyResult {
     let data: UserOrgTypeData = data.into_inner();
 
-    let mut user_to_edit = match UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &conn) {
+    let mut user_to_edit = match UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &conn).await {
         Some(user) => user,
         None => err!("The specified user isn't member of the organization"),
     };
@@ -409,7 +415,8 @@ fn update_user_org_type(data: Json<UserOrgTypeData>, _token: AdminToken, conn: D
 
     if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner {
         // Removing owner permmission, check that there are at least another owner
-        let num_owners = UserOrganization::find_by_org_and_type(&data.org_uuid, UserOrgType::Owner as i32, &conn).len();
+        let num_owners =
+            UserOrganization::find_by_org_and_type(&data.org_uuid, UserOrgType::Owner as i32, &conn).await.len();
 
         if num_owners <= 1 {
             err!("Can't change the type of the last owner")
@@ -417,37 +424,37 @@ fn update_user_org_type(data: Json<UserOrgTypeData>, _token: AdminToken, conn: D
     }
 
     user_to_edit.atype = new_type as i32;
-    user_to_edit.save(&conn)
+    user_to_edit.save(&conn).await
 }
 
 #[post("/users/update_revision")]
-fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
-    User::update_all_revisions(&conn)
+async fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
+    User::update_all_revisions(&conn).await
 }
 
 #[get("/organizations/overview")]
-fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
-    let organizations = Organization::get_all(&conn);
-    let organizations_json: Vec<Value> = organizations
-        .iter()
-        .map(|o| {
+async fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
+    let organizations_json = stream::iter(Organization::get_all(&conn).await)
+        .then(|o| async {
+            let o = o; //Move out this single variable
             let mut org = o.to_json();
-            org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &conn));
-            org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &conn));
-            org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &conn));
-            org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &conn) as i32));
+            org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &conn).await);
+            org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &conn).await);
+            org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &conn).await);
+            org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &conn).await as i32));
             org
         })
-        .collect();
+        .collect::<Vec<Value>>()
+        .await;
 
     let text = AdminTemplateData::with_data("admin/organizations", json!(organizations_json)).render()?;
     Ok(Html(text))
 }
 
 #[post("/organizations/<uuid>/delete")]
-fn delete_organization(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
-    let org = Organization::find_by_uuid(&uuid, &conn).map_res("Organization doesn't exist")?;
-    org.delete(&conn)
+async fn delete_organization(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
+    let org = Organization::find_by_uuid(&uuid, &conn).await.map_res("Organization doesn't exist")?;
+    org.delete(&conn).await
 }
 
 #[derive(Deserialize)]
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index d790f67d..af4e0796 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -63,11 +63,11 @@ struct KeysData {
 }
 
 #[post("/accounts/register", data = "<data>")]
-fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
+async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
     let data: RegisterData = data.into_inner().data;
     let email = data.Email.to_lowercase();
 
-    let mut user = match User::find_by_mail(&email, &conn) {
+    let mut user = match User::find_by_mail(&email, &conn).await {
         Some(user) => {
             if !user.password_hash.is_empty() {
                 if CONFIG.is_signup_allowed(&email) {
@@ -84,13 +84,13 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
                 } else {
                     err!("Registration email does not match invite email")
                 }
-            } else if Invitation::take(&email, &conn) {
-                for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() {
+            } else if Invitation::take(&email, &conn).await {
+                for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).await.iter_mut() {
                     user_org.status = UserOrgStatus::Accepted as i32;
-                    user_org.save(&conn)?;
+                    user_org.save(&conn).await?;
                 }
                 user
-            } else if EmergencyAccess::find_invited_by_grantee_email(&email, &conn).is_some() {
+            } else if EmergencyAccess::find_invited_by_grantee_email(&email, &conn).await.is_some() {
                 user
             } else if CONFIG.is_signup_allowed(&email) {
                 err!("Account with this email already exists")
@@ -102,7 +102,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
             // Order is important here; the invitation check must come first
             // because the vaultwarden admin can invite anyone, regardless
             // of other signup restrictions.
-            if Invitation::take(&email, &conn) || CONFIG.is_signup_allowed(&email) {
+            if Invitation::take(&email, &conn).await || CONFIG.is_signup_allowed(&email) {
                 User::new(email.clone())
             } else {
                 err!("Registration not allowed or user already exists")
@@ -111,7 +111,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
     };
 
     // Make sure we don't leave a lingering invitation.
-    Invitation::take(&email, &conn);
+    Invitation::take(&email, &conn).await;
 
     if let Some(client_kdf_iter) = data.KdfIterations {
         user.client_kdf_iter = client_kdf_iter;
@@ -150,12 +150,12 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
         }
     }
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[get("/accounts/profile")]
-fn profile(headers: Headers, conn: DbConn) -> Json<Value> {
-    Json(headers.user.to_json(&conn))
+async fn profile(headers: Headers, conn: DbConn) -> Json<Value> {
+    Json(headers.user.to_json(&conn).await)
 }
 
 #[derive(Deserialize, Debug)]
@@ -168,12 +168,12 @@ struct ProfileData {
 }
 
 #[put("/accounts/profile", data = "<data>")]
-fn put_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
-    post_profile(data, headers, conn)
+async fn put_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
+    post_profile(data, headers, conn).await
 }
 
 #[post("/accounts/profile", data = "<data>")]
-fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: ProfileData = data.into_inner().data;
 
     let mut user = headers.user;
@@ -183,13 +183,13 @@ fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -
         Some(ref h) if h.is_empty() => None,
         _ => data.MasterPasswordHint,
     };
-    user.save(&conn)?;
-    Ok(Json(user.to_json(&conn)))
+    user.save(&conn).await?;
+    Ok(Json(user.to_json(&conn).await))
 }
 
 #[get("/users/<uuid>/public-key")]
-fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonResult {
-    let user = match User::find_by_uuid(&uuid, &conn) {
+async fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonResult {
+    let user = match User::find_by_uuid(&uuid, &conn).await {
         Some(user) => user,
         None => err!("User doesn't exist"),
     };
@@ -202,7 +202,7 @@ fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonResult
 }
 
 #[post("/accounts/keys", data = "<data>")]
-fn post_keys(data: JsonUpcase<KeysData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn post_keys(data: JsonUpcase<KeysData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: KeysData = data.into_inner().data;
 
     let mut user = headers.user;
@@ -210,7 +210,7 @@ fn post_keys(data: JsonUpcase<KeysData>, headers: Headers, conn: DbConn) -> Json
     user.private_key = Some(data.EncryptedPrivateKey);
     user.public_key = Some(data.PublicKey);
 
-    user.save(&conn)?;
+    user.save(&conn).await?;
 
     Ok(Json(json!({
         "PrivateKey": user.private_key,
@@ -228,7 +228,7 @@ struct ChangePassData {
 }
 
 #[post("/accounts/password", data = "<data>")]
-fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: ChangePassData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -241,7 +241,7 @@ fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbCon
         Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]),
     );
     user.akey = data.Key;
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[derive(Deserialize)]
@@ -256,7 +256,7 @@ struct ChangeKdfData {
 }
 
 #[post("/accounts/kdf", data = "<data>")]
-fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: ChangeKdfData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -268,7 +268,7 @@ fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) ->
     user.client_kdf_type = data.Kdf;
     user.set_password(&data.NewMasterPasswordHash, None);
     user.akey = data.Key;
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[derive(Deserialize)]
@@ -291,7 +291,7 @@ struct KeyData {
 }
 
 #[post("/accounts/key", data = "<data>")]
-fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
+async fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
     let data: KeyData = data.into_inner().data;
 
     if !headers.user.check_valid_password(&data.MasterPasswordHash) {
@@ -302,7 +302,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt:
 
     // Update folder data
     for folder_data in data.Folders {
-        let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &conn) {
+        let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &conn).await {
             Some(folder) => folder,
             None => err!("Folder doesn't exist"),
         };
@@ -312,14 +312,14 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt:
         }
 
         saved_folder.name = folder_data.Name;
-        saved_folder.save(&conn)?
+        saved_folder.save(&conn).await?
     }
 
     // Update cipher data
     use super::ciphers::update_cipher_from_data;
 
     for cipher_data in data.Ciphers {
-        let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.Id.as_ref().unwrap(), &conn) {
+        let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.Id.as_ref().unwrap(), &conn).await {
             Some(cipher) => cipher,
             None => err!("Cipher doesn't exist"),
         };
@@ -330,7 +330,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt:
 
         // Prevent triggering cipher updates via WebSockets by settings UpdateType::None
         // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues.
-        update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None)?
+        update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await?
     }
 
     // Update user data
@@ -340,11 +340,11 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt:
     user.private_key = Some(data.PrivateKey);
     user.reset_security_stamp();
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[post("/accounts/security-stamp", data = "<data>")]
-fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: PasswordData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -352,9 +352,9 @@ fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -
         err!("Invalid password")
     }
 
-    Device::delete_all_by_user(&user.uuid, &conn)?;
+    Device::delete_all_by_user(&user.uuid, &conn).await?;
     user.reset_security_stamp();
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[derive(Deserialize)]
@@ -365,7 +365,7 @@ struct EmailTokenData {
 }
 
 #[post("/accounts/email-token", data = "<data>")]
-fn post_email_token(data: JsonUpcase<EmailTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn post_email_token(data: JsonUpcase<EmailTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: EmailTokenData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -373,7 +373,7 @@ fn post_email_token(data: JsonUpcase<EmailTokenData>, headers: Headers, conn: Db
         err!("Invalid password")
     }
 
-    if User::find_by_mail(&data.NewEmail, &conn).is_some() {
+    if User::find_by_mail(&data.NewEmail, &conn).await.is_some() {
         err!("Email already in use");
     }
 
@@ -391,7 +391,7 @@ fn post_email_token(data: JsonUpcase<EmailTokenData>, headers: Headers, conn: Db
 
     user.email_new = Some(data.NewEmail);
     user.email_new_token = Some(token);
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[derive(Deserialize)]
@@ -406,7 +406,7 @@ struct ChangeEmailData {
 }
 
 #[post("/accounts/email", data = "<data>")]
-fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: ChangeEmailData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -414,7 +414,7 @@ fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn)
         err!("Invalid password")
     }
 
-    if User::find_by_mail(&data.NewEmail, &conn).is_some() {
+    if User::find_by_mail(&data.NewEmail, &conn).await.is_some() {
         err!("Email already in use");
     }
 
@@ -449,7 +449,7 @@ fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn)
     user.set_password(&data.NewMasterPasswordHash, None);
     user.akey = data.Key;
 
-    user.save(&conn)
+    user.save(&conn).await
 }
 
 #[post("/accounts/verify-email")]
@@ -475,10 +475,10 @@ struct VerifyEmailTokenData {
 }
 
 #[post("/accounts/verify-email-token", data = "<data>")]
-fn post_verify_email_token(data: JsonUpcase<VerifyEmailTokenData>, conn: DbConn) -> EmptyResult {
+async fn post_verify_email_token(data: JsonUpcase<VerifyEmailTokenData>, conn: DbConn) -> EmptyResult {
     let data: VerifyEmailTokenData = data.into_inner().data;
 
-    let mut user = match User::find_by_uuid(&data.UserId, &conn) {
+    let mut user = match User::find_by_uuid(&data.UserId, &conn).await {
         Some(user) => user,
         None => err!("User doesn't exist"),
     };
@@ -493,7 +493,7 @@ fn post_verify_email_token(data: JsonUpcase<VerifyEmailTokenData>, conn: DbConn)
     user.verified_at = Some(Utc::now().naive_utc());
     user.last_verifying_at = None;
     user.login_verify_count = 0;
-    if let Err(e) = user.save(&conn) {
+    if let Err(e) = user.save(&conn).await {
         error!("Error saving email verification: {:#?}", e);
     }
 
@@ -507,13 +507,11 @@ struct DeleteRecoverData {
 }
 
 #[post("/accounts/delete-recover", data = "<data>")]
-fn post_delete_recover(data: JsonUpcase<DeleteRecoverData>, conn: DbConn) -> EmptyResult {
+async fn post_delete_recover(data: JsonUpcase<DeleteRecoverData>, conn: DbConn) -> EmptyResult {
     let data: DeleteRecoverData = data.into_inner().data;
 
-    let user = User::find_by_mail(&data.Email, &conn);
-
     if CONFIG.mail_enabled() {
-        if let Some(user) = user {
+        if let Some(user) = User::find_by_mail(&data.Email, &conn).await {
             if let Err(e) = mail::send_delete_account(&user.email, &user.uuid) {
                 error!("Error sending delete account email: {:#?}", e);
             }
@@ -536,10 +534,10 @@ struct DeleteRecoverTokenData {
 }
 
 #[post("/accounts/delete-recover-token", data = "<data>")]
-fn post_delete_recover_token(data: JsonUpcase<DeleteRecoverTokenData>, conn: DbConn) -> EmptyResult {
+async fn post_delete_recover_token(data: JsonUpcase<DeleteRecoverTokenData>, conn: DbConn) -> EmptyResult {
     let data: DeleteRecoverTokenData = data.into_inner().data;
 
-    let user = match User::find_by_uuid(&data.UserId, &conn) {
+    let user = match User::find_by_uuid(&data.UserId, &conn).await {
         Some(user) => user,
         None => err!("User doesn't exist"),
     };
@@ -551,16 +549,16 @@ fn post_delete_recover_token(data: JsonUpcase<DeleteRecoverTokenData>, conn: DbC
     if claims.sub != user.uuid {
         err!("Invalid claim");
     }
-    user.delete(&conn)
+    user.delete(&conn).await
 }
 
 #[post("/accounts/delete", data = "<data>")]
-fn post_delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
-    delete_account(data, headers, conn)
+async fn post_delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
+    delete_account(data, headers, conn).await
 }
 
 #[delete("/accounts", data = "<data>")]
-fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: PasswordData = data.into_inner().data;
     let user = headers.user;
 
@@ -568,7 +566,7 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
         err!("Invalid password")
     }
 
-    user.delete(&conn)
+    user.delete(&conn).await
 }
 
 #[get("/accounts/revision-date")]
@@ -584,7 +582,7 @@ struct PasswordHintData {
 }
 
 #[post("/accounts/password-hint", data = "<data>")]
-fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult {
+async fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult {
     if !CONFIG.mail_enabled() && !CONFIG.show_password_hint() {
         err!("This server is not configured to provide password hints.");
     }
@@ -594,7 +592,7 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
     let data: PasswordHintData = data.into_inner().data;
     let email = &data.Email;
 
-    match User::find_by_mail(email, &conn) {
+    match User::find_by_mail(email, &conn).await {
         None => {
             // To prevent user enumeration, act as if the user exists.
             if CONFIG.mail_enabled() {
@@ -633,10 +631,10 @@ struct PreloginData {
 }
 
 #[post("/accounts/prelogin", data = "<data>")]
-fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> {
+async fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> {
     let data: PreloginData = data.into_inner().data;
 
-    let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn) {
+    let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn).await {
         Some(user) => (user.client_kdf_type, user.client_kdf_iter),
         None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
     };
@@ -666,7 +664,7 @@ fn verify_password(data: JsonUpcase<SecretVerificationRequest>, headers: Headers
     Ok(())
 }
 
-fn _api_key(data: JsonUpcase<SecretVerificationRequest>, rotate: bool, headers: Headers, conn: DbConn) -> JsonResult {
+async fn _api_key(data: JsonUpcase<SecretVerificationRequest>, rotate: bool, headers: Headers, conn: DbConn) -> JsonResult {
     let data: SecretVerificationRequest = data.into_inner().data;
     let mut user = headers.user;
 
@@ -676,7 +674,7 @@ fn _api_key(data: JsonUpcase<SecretVerificationRequest>, rotate: bool, headers:
 
     if rotate || user.api_key.is_none() {
         user.api_key = Some(crypto::generate_api_key());
-        user.save(&conn).expect("Error saving API key");
+        user.save(&conn).await.expect("Error saving API key");
     }
 
     Ok(Json(json!({
@@ -686,11 +684,11 @@ fn _api_key(data: JsonUpcase<SecretVerificationRequest>, rotate: bool, headers:
 }
 
 #[post("/accounts/api-key", data = "<data>")]
-fn api_key(data: JsonUpcase<SecretVerificationRequest>, headers: Headers, conn: DbConn) -> JsonResult {
-    _api_key(data, false, headers, conn)
+async fn api_key(data: JsonUpcase<SecretVerificationRequest>, headers: Headers, conn: DbConn) -> JsonResult {
+    _api_key(data, false, headers, conn).await
 }
 
 #[post("/accounts/rotate-api-key", data = "<data>")]
-fn rotate_api_key(data: JsonUpcase<SecretVerificationRequest>, headers: Headers, conn: DbConn) -> JsonResult {
-    _api_key(data, true, headers, conn)
+async fn rotate_api_key(data: JsonUpcase<SecretVerificationRequest>, headers: Headers, conn: DbConn) -> JsonResult {
+    _api_key(data, true, headers, conn).await
 }
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
index 1e6d6b1b..b84002aa 100644
--- a/src/api/core/ciphers.rs
+++ b/src/api/core/ciphers.rs
@@ -17,6 +17,8 @@ use crate::{
     CONFIG,
 };
 
+use futures::{stream, stream::StreamExt};
+
 pub fn routes() -> Vec<Route> {
     // Note that many routes have an `admin` variant; this seems to be
     // because the stored procedure that upstream Bitwarden uses to determine
@@ -83,7 +85,7 @@ pub fn routes() -> Vec<Route> {
 pub async fn purge_trashed_ciphers(pool: DbPool) {
     debug!("Purging trashed ciphers");
     if let Ok(conn) = pool.get().await {
-        Cipher::purge_trash(&conn);
+        Cipher::purge_trash(&conn).await;
     } else {
         error!("Failed to get DB connection while purging trashed ciphers")
     }
@@ -96,25 +98,33 @@ struct SyncData {
 }
 
 #[get("/sync?<data..>")]
-fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> {
-    let user_json = headers.user.to_json(&conn);
+async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> {
+    let user_json = headers.user.to_json(&conn).await;
 
-    let folders = Folder::find_by_user(&headers.user.uuid, &conn);
+    let folders = Folder::find_by_user(&headers.user.uuid, &conn).await;
     let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
 
-    let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn);
-    let collections_json: Vec<Value> =
-        collections.iter().map(|c| c.to_json_details(&headers.user.uuid, &conn)).collect();
+    let collections_json = stream::iter(Collection::find_by_user_uuid(&headers.user.uuid, &conn).await)
+        .then(|c| async {
+            let c = c; // Move out this single variable
+            c.to_json_details(&headers.user.uuid, &conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     let policies = OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &conn);
-    let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
+    let policies_json: Vec<Value> = policies.await.iter().map(OrgPolicy::to_json).collect();
 
-    let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
-    let ciphers_json: Vec<Value> =
-        ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
+    let ciphers_json = stream::iter(Cipher::find_by_user_visible(&headers.user.uuid, &conn).await)
+        .then(|c| async {
+            let c = c; // Move out this single variable
+            c.to_json(&headers.host, &headers.user.uuid, &conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     let sends = Send::find_by_user(&headers.user.uuid, &conn);
-    let sends_json: Vec<Value> = sends.iter().map(|s| s.to_json()).collect();
+    let sends_json: Vec<Value> = sends.await.iter().map(|s| s.to_json()).collect();
 
     let domains_json = if data.exclude_domains {
         Value::Null
@@ -136,11 +146,14 @@ fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> {
 }
 
 #[get("/ciphers")]
-fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
-    let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
-
-    let ciphers_json: Vec<Value> =
-        ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
+async fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
+    let ciphers_json = stream::iter(Cipher::find_by_user_visible(&headers.user.uuid, &conn).await)
+        .then(|c| async {
+            let c = c; // Move out this single variable
+            c.to_json(&headers.host, &headers.user.uuid, &conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     Json(json!({
       "Data": ciphers_json,
@@ -150,28 +163,28 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
 }
 
 #[get("/ciphers/<uuid>")]
-fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
-    let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
+async fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
+    let cipher = match Cipher::find_by_uuid(&uuid, &conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
+    if !cipher.is_accessible_to_user(&headers.user.uuid, &conn).await {
         err!("Cipher is not owned by user")
     }
 
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn).await))
 }
 
 #[get("/ciphers/<uuid>/admin")]
-fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
     // TODO: Implement this correctly
-    get_cipher(uuid, headers, conn)
+    get_cipher(uuid, headers, conn).await
 }
 
 #[get("/ciphers/<uuid>/details")]
-fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
-    get_cipher(uuid, headers, conn)
+async fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
+    get_cipher(uuid, headers, conn).await
 }
 
 #[derive(Deserialize, Debug)]
@@ -229,15 +242,25 @@ pub struct Attachments2Data {
 
 /// Called when an org admin clones an org cipher.
 #[post("/ciphers/admin", data = "<data>")]
-fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    post_ciphers_create(data, headers, conn, nt)
+async fn post_ciphers_admin(
+    data: JsonUpcase<ShareCipherData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    post_ciphers_create(data, headers, conn, nt).await
 }
 
 /// Called when creating a new org-owned cipher, or cloning a cipher (whether
 /// user- or org-owned). When cloning a cipher to a user-owned cipher,
 /// `organizationId` is null.
 #[post("/ciphers/create", data = "<data>")]
-fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
+async fn post_ciphers_create(
+    data: JsonUpcase<ShareCipherData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
     let mut data: ShareCipherData = data.into_inner().data;
 
     // Check if there are one more more collections selected when this cipher is part of an organization.
@@ -249,11 +272,11 @@ fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn
     // This check is usually only needed in update_cipher_from_data(), but we
     // need it here as well to avoid creating an empty cipher in the call to
     // cipher.save() below.
-    enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &conn)?;
+    enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &conn).await?;
 
     let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
     cipher.user_uuid = Some(headers.user.uuid.clone());
-    cipher.save(&conn)?;
+    cipher.save(&conn).await?;
 
     // When cloning a cipher, the Bitwarden clients seem to set this field
     // based on the cipher being cloned (when creating a new cipher, it's set
@@ -263,12 +286,12 @@ fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn
     // or otherwise), we can just ignore this field entirely.
     data.Cipher.LastKnownRevisionDate = None;
 
-    share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt)
+    share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt).await
 }
 
 /// Called when creating a new user-owned cipher.
 #[post("/ciphers", data = "<data>")]
-fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
+async fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
     let mut data: CipherData = data.into_inner().data;
 
     // The web/browser clients set this field to null as expected, but the
@@ -278,9 +301,9 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt
     data.LastKnownRevisionDate = None;
 
     let mut cipher = Cipher::new(data.Type, data.Name.clone());
-    update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate)?;
+    update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate).await?;
 
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn).await))
 }
 
 /// Enforces the personal ownership policy on user-owned ciphers, if applicable.
@@ -290,27 +313,27 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt
 /// allowed to delete or share such ciphers to an org, however.
 ///
 /// Ref: https://bitwarden.com/help/article/policies/#personal-ownership
-fn enforce_personal_ownership_policy(data: Option<&CipherData>, headers: &Headers, conn: &DbConn) -> EmptyResult {
+async fn enforce_personal_ownership_policy(data: Option<&CipherData>, headers: &Headers, conn: &DbConn) -> EmptyResult {
     if data.is_none() || data.unwrap().OrganizationId.is_none() {
         let user_uuid = &headers.user.uuid;
         let policy_type = OrgPolicyType::PersonalOwnership;
-        if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) {
+        if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn).await {
             err!("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.")
         }
     }
     Ok(())
 }
 
-pub fn update_cipher_from_data(
+pub async fn update_cipher_from_data(
     cipher: &mut Cipher,
     data: CipherData,
     headers: &Headers,
     shared_to_collection: bool,
     conn: &DbConn,
-    nt: &Notify,
+    nt: &Notify<'_>,
     ut: UpdateType,
 ) -> EmptyResult {
-    enforce_personal_ownership_policy(Some(&data), headers, conn)?;
+    enforce_personal_ownership_policy(Some(&data), headers, conn).await?;
 
     // Check that the client isn't updating an existing cipher with stale data.
     if let Some(dt) = data.LastKnownRevisionDate {
@@ -329,12 +352,12 @@ pub fn update_cipher_from_data(
     }
 
     if let Some(org_id) = data.OrganizationId {
-        match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, conn) {
+        match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await {
             None => err!("You don't have permission to add item to organization"),
             Some(org_user) => {
                 if shared_to_collection
                     || org_user.has_full_access()
-                    || cipher.is_write_accessible_to_user(&headers.user.uuid, conn)
+                    || cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await
                 {
                     cipher.organization_uuid = Some(org_id);
                     // After some discussion in PR #1329 re-added the user_uuid = None again.
@@ -353,7 +376,7 @@ pub fn update_cipher_from_data(
     }
 
     if let Some(ref folder_id) = data.FolderId {
-        match Folder::find_by_uuid(folder_id, conn) {
+        match Folder::find_by_uuid(folder_id, conn).await {
             Some(folder) => {
                 if folder.user_uuid != headers.user.uuid {
                     err!("Folder is not owned by user")
@@ -366,7 +389,7 @@ pub fn update_cipher_from_data(
     // Modify attachments name and keys when rotating
     if let Some(attachments) = data.Attachments2 {
         for (id, attachment) in attachments {
-            let mut saved_att = match Attachment::find_by_id(&id, conn) {
+            let mut saved_att = match Attachment::find_by_id(&id, conn).await {
                 Some(att) => att,
                 None => err!("Attachment doesn't exist"),
             };
@@ -381,7 +404,7 @@ pub fn update_cipher_from_data(
             saved_att.akey = Some(attachment.Key);
             saved_att.file_name = attachment.FileName;
 
-            saved_att.save(conn)?;
+            saved_att.save(conn).await?;
         }
     }
 
@@ -427,12 +450,12 @@ pub fn update_cipher_from_data(
     cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
     cipher.reprompt = data.Reprompt;
 
-    cipher.save(conn)?;
-    cipher.move_to_folder(data.FolderId, &headers.user.uuid, conn)?;
-    cipher.set_favorite(data.Favorite, &headers.user.uuid, conn)?;
+    cipher.save(conn).await?;
+    cipher.move_to_folder(data.FolderId, &headers.user.uuid, conn).await?;
+    cipher.set_favorite(data.Favorite, &headers.user.uuid, conn).await?;
 
     if ut != UpdateType::None {
-        nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn));
+        nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn).await);
     }
 
     Ok(())
@@ -458,8 +481,13 @@ struct RelationsData {
 }
 
 #[post("/ciphers/import", data = "<data>")]
-fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    enforce_personal_ownership_policy(None, &headers, &conn)?;
+async fn post_ciphers_import(
+    data: JsonUpcase<ImportData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    enforce_personal_ownership_policy(None, &headers, &conn).await?;
 
     let data: ImportData = data.into_inner().data;
 
@@ -467,7 +495,7 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
     let mut folders: Vec<_> = Vec::new();
     for folder in data.Folders.into_iter() {
         let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name);
-        new_folder.save(&conn)?;
+        new_folder.save(&conn).await?;
 
         folders.push(new_folder);
     }
@@ -485,48 +513,60 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
         cipher_data.FolderId = folder_uuid;
 
         let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
-        update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None)?;
+        update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await?;
     }
 
     let mut user = headers.user;
-    user.update_revision(&conn)?;
+    user.update_revision(&conn).await?;
     nt.send_user_update(UpdateType::Vault, &user);
     Ok(())
 }
 
 /// Called when an org admin modifies an existing org cipher.
 #[put("/ciphers/<uuid>/admin", data = "<data>")]
-fn put_cipher_admin(
+async fn put_cipher_admin(
     uuid: String,
     data: JsonUpcase<CipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> JsonResult {
-    put_cipher(uuid, data, headers, conn, nt)
+    put_cipher(uuid, data, headers, conn, nt).await
 }
 
 #[post("/ciphers/<uuid>/admin", data = "<data>")]
-fn post_cipher_admin(
+async fn post_cipher_admin(
     uuid: String,
     data: JsonUpcase<CipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> JsonResult {
-    post_cipher(uuid, data, headers, conn, nt)
+    post_cipher(uuid, data, headers, conn, nt).await
 }
 
 #[post("/ciphers/<uuid>", data = "<data>")]
-fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    put_cipher(uuid, data, headers, conn, nt)
+async fn post_cipher(
+    uuid: String,
+    data: JsonUpcase<CipherData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    put_cipher(uuid, data, headers, conn, nt).await
 }
 
 #[put("/ciphers/<uuid>", data = "<data>")]
-fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
+async fn put_cipher(
+    uuid: String,
+    data: JsonUpcase<CipherData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
     let data: CipherData = data.into_inner().data;
 
-    let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
+    let mut cipher = match Cipher::find_by_uuid(&uuid, &conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
@@ -536,13 +576,13 @@ fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn
     // cipher itself, so the user shouldn't need write access to change these.
     // Interestingly, upstream Bitwarden doesn't properly handle this either.
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await {
         err!("Cipher is not write accessible")
     }
 
-    update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?;
+    update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate).await?;
 
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn).await))
 }
 
 #[derive(Deserialize)]
@@ -552,37 +592,37 @@ struct CollectionsAdminData {
 }
 
 #[put("/ciphers/<uuid>/collections", data = "<data>")]
-fn put_collections_update(
+async fn put_collections_update(
     uuid: String,
     data: JsonUpcase<CollectionsAdminData>,
     headers: Headers,
     conn: DbConn,
 ) -> EmptyResult {
-    post_collections_admin(uuid, data, headers, conn)
+    post_collections_admin(uuid, data, headers, conn).await
 }
 
 #[post("/ciphers/<uuid>/collections", data = "<data>")]
-fn post_collections_update(
+async fn post_collections_update(
     uuid: String,
     data: JsonUpcase<CollectionsAdminData>,
     headers: Headers,
     conn: DbConn,
 ) -> EmptyResult {
-    post_collections_admin(uuid, data, headers, conn)
+    post_collections_admin(uuid, data, headers, conn).await
 }
 
 #[put("/ciphers/<uuid>/collections-admin", data = "<data>")]
-fn put_collections_admin(
+async fn put_collections_admin(
     uuid: String,
     data: JsonUpcase<CollectionsAdminData>,
     headers: Headers,
     conn: DbConn,
 ) -> EmptyResult {
-    post_collections_admin(uuid, data, headers, conn)
+    post_collections_admin(uuid, data, headers, conn).await
 }
 
 #[post("/ciphers/<uuid>/collections-admin", data = "<data>")]
-fn post_collections_admin(
+async fn post_collections_admin(
     uuid: String,
     data: JsonUpcase<CollectionsAdminData>,
     headers: Headers,
@@ -590,30 +630,30 @@ fn post_collections_admin(
 ) -> EmptyResult {
     let data: CollectionsAdminData = data.into_inner().data;
 
-    let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
+    let cipher = match Cipher::find_by_uuid(&uuid, &conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await {
         err!("Cipher is not write accessible")
     }
 
     let posted_collections: HashSet<String> = data.CollectionIds.iter().cloned().collect();
     let current_collections: HashSet<String> =
-        cipher.get_collections(&headers.user.uuid, &conn).iter().cloned().collect();
+        cipher.get_collections(&headers.user.uuid, &conn).await.iter().cloned().collect();
 
     for collection in posted_collections.symmetric_difference(&current_collections) {
-        match Collection::find_by_uuid(collection, &conn) {
+        match Collection::find_by_uuid(collection, &conn).await {
             None => err!("Invalid collection ID provided"),
             Some(collection) => {
-                if collection.is_writable_by_user(&headers.user.uuid, &conn) {
+                if collection.is_writable_by_user(&headers.user.uuid, &conn).await {
                     if posted_collections.contains(&collection.uuid) {
                         // Add to collection
-                        CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn)?;
+                        CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn).await?;
                     } else {
                         // Remove from collection
-                        CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn)?;
+                        CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn).await?;
                     }
                 } else {
                     err!("No rights to modify the collection")
@@ -633,29 +673,29 @@ struct ShareCipherData {
 }
 
 #[post("/ciphers/<uuid>/share", data = "<data>")]
-fn post_cipher_share(
+async fn post_cipher_share(
     uuid: String,
     data: JsonUpcase<ShareCipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> JsonResult {
     let data: ShareCipherData = data.into_inner().data;
 
-    share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
+    share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt).await
 }
 
 #[put("/ciphers/<uuid>/share", data = "<data>")]
-fn put_cipher_share(
+async fn put_cipher_share(
     uuid: String,
     data: JsonUpcase<ShareCipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> JsonResult {
     let data: ShareCipherData = data.into_inner().data;
 
-    share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt)
+    share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt).await
 }
 
 #[derive(Deserialize)]
@@ -666,11 +706,11 @@ struct ShareSelectedCipherData {
 }
 
 #[put("/ciphers/share", data = "<data>")]
-fn put_cipher_share_selected(
+async fn put_cipher_share_selected(
     data: JsonUpcase<ShareSelectedCipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let mut data: ShareSelectedCipherData = data.into_inner().data;
     let mut cipher_ids: Vec<String> = Vec::new();
@@ -697,7 +737,7 @@ fn put_cipher_share_selected(
         };
 
         match shared_cipher_data.Cipher.Id.take() {
-            Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt)?,
+            Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt).await?,
             None => err!("Request missing ids field"),
         };
     }
@@ -705,16 +745,16 @@ fn put_cipher_share_selected(
     Ok(())
 }
 
-fn share_cipher_by_uuid(
+async fn share_cipher_by_uuid(
     uuid: &str,
     data: ShareCipherData,
     headers: &Headers,
     conn: &DbConn,
-    nt: &Notify,
+    nt: &Notify<'_>,
 ) -> JsonResult {
-    let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
+    let mut cipher = match Cipher::find_by_uuid(uuid, conn).await {
         Some(cipher) => {
-            if cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
+            if cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await {
                 cipher
             } else {
                 err!("Cipher is not write accessible")
@@ -731,11 +771,11 @@ fn share_cipher_by_uuid(
         None => {}
         Some(organization_uuid) => {
             for uuid in &data.CollectionIds {
-                match Collection::find_by_uuid_and_org(uuid, &organization_uuid, conn) {
+                match Collection::find_by_uuid_and_org(uuid, &organization_uuid, conn).await {
                     None => err!("Invalid collection ID provided"),
                     Some(collection) => {
-                        if collection.is_writable_by_user(&headers.user.uuid, conn) {
-                            CollectionCipher::save(&cipher.uuid, &collection.uuid, conn)?;
+                        if collection.is_writable_by_user(&headers.user.uuid, conn).await {
+                            CollectionCipher::save(&cipher.uuid, &collection.uuid, conn).await?;
                             shared_to_collection = true;
                         } else {
                             err!("No rights to modify the collection")
@@ -754,9 +794,10 @@ fn share_cipher_by_uuid(
         conn,
         nt,
         UpdateType::CipherUpdate,
-    )?;
+    )
+    .await?;
 
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn)))
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn).await))
 }
 
 /// v2 API for downloading an attachment. This just redirects the client to
@@ -766,8 +807,8 @@ fn share_cipher_by_uuid(
 /// their object storage service. For self-hosted instances, it basically just
 /// redirects to the same location as before the v2 API.
 #[get("/ciphers/<uuid>/attachment/<attachment_id>")]
-fn get_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> JsonResult {
-    match Attachment::find_by_id(&attachment_id, &conn) {
+async fn get_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+    match Attachment::find_by_id(&attachment_id, &conn).await {
         Some(attachment) if uuid == attachment.cipher_uuid => Ok(Json(attachment.to_json(&headers.host))),
         Some(_) => err!("Attachment doesn't belong to cipher"),
         None => err!("Attachment doesn't exist"),
@@ -793,18 +834,18 @@ enum FileUploadType {
 /// For upstream's cloud-hosted service, it's an Azure object storage API.
 /// For self-hosted instances, it's another API on the local instance.
 #[post("/ciphers/<uuid>/attachment/v2", data = "<data>")]
-fn post_attachment_v2(
+async fn post_attachment_v2(
     uuid: String,
     data: JsonUpcase<AttachmentRequestData>,
     headers: Headers,
     conn: DbConn,
 ) -> JsonResult {
-    let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
+    let cipher = match Cipher::find_by_uuid(&uuid, &conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await {
         err!("Cipher is not write accessible")
     }
 
@@ -812,7 +853,7 @@ fn post_attachment_v2(
     let data: AttachmentRequestData = data.into_inner().data;
     let attachment =
         Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.FileName, data.FileSize, Some(data.Key));
-    attachment.save(&conn).expect("Error saving attachment");
+    attachment.save(&conn).await.expect("Error saving attachment");
 
     let url = format!("/ciphers/{}/attachment/{}", cipher.uuid, attachment_id);
     let response_key = match data.AdminRequest {
@@ -825,7 +866,7 @@ fn post_attachment_v2(
         "AttachmentId": attachment_id,
         "Url": url,
         "FileUploadType": FileUploadType::Direct as i32,
-        response_key: cipher.to_json(&headers.host, &headers.user.uuid, &conn),
+        response_key: cipher.to_json(&headers.host, &headers.user.uuid, &conn).await,
     })))
 }
 
@@ -851,12 +892,12 @@ async fn save_attachment(
     conn: DbConn,
     nt: Notify<'_>,
 ) -> Result<(Cipher, DbConn), crate::error::Error> {
-    let cipher = match Cipher::find_by_uuid(&cipher_uuid, &conn) {
+    let cipher = match Cipher::find_by_uuid(&cipher_uuid, &conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await {
         err!("Cipher is not write accessible")
     }
 
@@ -871,7 +912,7 @@ async fn save_attachment(
         match CONFIG.user_attachment_limit() {
             Some(0) => err!("Attachments are disabled"),
             Some(limit_kb) => {
-                let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &conn) + size_adjust;
+                let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &conn).await + size_adjust;
                 if left <= 0 {
                     err!("Attachment storage limit reached! Delete some attachments to free up space")
                 }
@@ -883,7 +924,7 @@ async fn save_attachment(
         match CONFIG.org_attachment_limit() {
             Some(0) => err!("Attachments are disabled"),
             Some(limit_kb) => {
-                let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &conn) + size_adjust;
+                let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &conn).await + size_adjust;
                 if left <= 0 {
                     err!("Attachment storage limit reached! Delete some attachments to free up space")
                 }
@@ -927,10 +968,10 @@ async fn save_attachment(
             if size != attachment.file_size {
                 // Update the attachment with the actual file size.
                 attachment.file_size = size;
-                attachment.save(&conn).expect("Error updating attachment");
+                attachment.save(&conn).await.expect("Error updating attachment");
             }
         } else {
-            attachment.delete(&conn).ok();
+            attachment.delete(&conn).await.ok();
 
             err!(format!("Attachment size mismatch (expected within [{}, {}], got {})", min_size, max_size, size));
         }
@@ -945,12 +986,12 @@ async fn save_attachment(
             err!("No attachment key provided")
         }
         let attachment = Attachment::new(file_id, cipher_uuid.clone(), encrypted_filename.unwrap(), size, data.key);
-        attachment.save(&conn).expect("Error saving attachment");
+        attachment.save(&conn).await.expect("Error saving attachment");
     }
 
     data.data.persist_to(file_path).await?;
 
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
+    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn).await);
 
     Ok((cipher, conn))
 }
@@ -968,7 +1009,7 @@ async fn post_attachment_v2_data(
     conn: DbConn,
     nt: Notify<'_>,
 ) -> EmptyResult {
-    let attachment = match Attachment::find_by_id(&attachment_id, &conn) {
+    let attachment = match Attachment::find_by_id(&attachment_id, &conn).await {
         Some(attachment) if uuid == attachment.cipher_uuid => Some(attachment),
         Some(_) => err!("Attachment doesn't belong to cipher"),
         None => err!("Attachment doesn't exist"),
@@ -994,7 +1035,7 @@ async fn post_attachment(
 
     let (cipher, conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?;
 
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn).await))
 }
 
 #[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
@@ -1017,131 +1058,162 @@ async fn post_attachment_share(
     conn: DbConn,
     nt: Notify<'_>,
 ) -> JsonResult {
-    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)?;
+    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await?;
     post_attachment(uuid, data, headers, conn, nt).await
 }
 
 #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
-fn delete_attachment_post_admin(
+async fn delete_attachment_post_admin(
     uuid: String,
     attachment_id: String,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    delete_attachment(uuid, attachment_id, headers, conn, nt)
+    delete_attachment(uuid, attachment_id, headers, conn, nt).await
 }
 
 #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
-fn delete_attachment_post(
+async fn delete_attachment_post(
     uuid: String,
     attachment_id: String,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    delete_attachment(uuid, attachment_id, headers, conn, nt)
+    delete_attachment(uuid, attachment_id, headers, conn, nt).await
 }
 
 #[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
-fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
+async fn delete_attachment(
+    uuid: String,
+    attachment_id: String,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await
 }
 
 #[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")]
-fn delete_attachment_admin(
+async fn delete_attachment_admin(
     uuid: String,
     attachment_id: String,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)
+    _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await
 }
 
 #[post("/ciphers/<uuid>/delete")]
-fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
+async fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await
 }
 
 #[post("/ciphers/<uuid>/delete-admin")]
-fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
+async fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await
 }
 
 #[put("/ciphers/<uuid>/delete")]
-fn delete_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt)
+async fn delete_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt).await
 }
 
 #[put("/ciphers/<uuid>/delete-admin")]
-fn delete_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt)
+async fn delete_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt).await
 }
 
 #[delete("/ciphers/<uuid>")]
-fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
+async fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await
 }
 
 #[delete("/ciphers/<uuid>/admin")]
-fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt)
+async fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await
 }
 
 #[delete("/ciphers", data = "<data>")]
-fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_multiple_ciphers(data, headers, conn, false, nt)
+async fn delete_cipher_selected(
+    data: JsonUpcase<Value>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    _delete_multiple_ciphers(data, headers, conn, false, nt).await
 }
 
 #[post("/ciphers/delete", data = "<data>")]
-fn delete_cipher_selected_post(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_multiple_ciphers(data, headers, conn, false, nt)
+async fn delete_cipher_selected_post(
+    data: JsonUpcase<Value>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    _delete_multiple_ciphers(data, headers, conn, false, nt).await
 }
 
 #[put("/ciphers/delete", data = "<data>")]
-fn delete_cipher_selected_put(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    _delete_multiple_ciphers(data, headers, conn, true, nt) // soft delete
+async fn delete_cipher_selected_put(
+    data: JsonUpcase<Value>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete
 }
 
 #[delete("/ciphers/admin", data = "<data>")]
-fn delete_cipher_selected_admin(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    delete_cipher_selected(data, headers, conn, nt)
+async fn delete_cipher_selected_admin(
+    data: JsonUpcase<Value>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
+    delete_cipher_selected(data, headers, conn, nt).await
 }
 
 #[post("/ciphers/delete-admin", data = "<data>")]
-fn delete_cipher_selected_post_admin(
+async fn delete_cipher_selected_post_admin(
     data: JsonUpcase<Value>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    delete_cipher_selected_post(data, headers, conn, nt)
+    delete_cipher_selected_post(data, headers, conn, nt).await
 }
 
 #[put("/ciphers/delete-admin", data = "<data>")]
-fn delete_cipher_selected_put_admin(
+async fn delete_cipher_selected_put_admin(
     data: JsonUpcase<Value>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    delete_cipher_selected_put(data, headers, conn, nt)
+    delete_cipher_selected_put(data, headers, conn, nt).await
 }
 
 #[put("/ciphers/<uuid>/restore")]
-fn restore_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt)
+async fn restore_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
+    _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt).await
 }
 
 #[put("/ciphers/<uuid>/restore-admin")]
-fn restore_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt)
+async fn restore_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
+    _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt).await
 }
 
 #[put("/ciphers/restore", data = "<data>")]
-fn restore_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    _restore_multiple_ciphers(data, &headers, &conn, &nt)
+async fn restore_cipher_selected(
+    data: JsonUpcase<Value>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    _restore_multiple_ciphers(data, &headers, &conn, &nt).await
 }
 
 #[derive(Deserialize)]
@@ -1152,12 +1224,17 @@ struct MoveCipherData {
 }
 
 #[post("/ciphers/move", data = "<data>")]
-fn move_cipher_selected(data: JsonUpcase<MoveCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
+async fn move_cipher_selected(
+    data: JsonUpcase<MoveCipherData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
     let data = data.into_inner().data;
     let user_uuid = headers.user.uuid;
 
     if let Some(ref folder_id) = data.FolderId {
-        match Folder::find_by_uuid(folder_id, &conn) {
+        match Folder::find_by_uuid(folder_id, &conn).await {
             Some(folder) => {
                 if folder.user_uuid != user_uuid {
                     err!("Folder is not owned by user")
@@ -1168,17 +1245,17 @@ fn move_cipher_selected(data: JsonUpcase<MoveCipherData>, headers: Headers, conn
     }
 
     for uuid in data.Ids {
-        let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
+        let cipher = match Cipher::find_by_uuid(&uuid, &conn).await {
             Some(cipher) => cipher,
             None => err!("Cipher doesn't exist"),
         };
 
-        if !cipher.is_accessible_to_user(&user_uuid, &conn) {
+        if !cipher.is_accessible_to_user(&user_uuid, &conn).await {
             err!("Cipher is not accessible by user")
         }
 
         // Move cipher
-        cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn)?;
+        cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn).await?;
 
         nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &[user_uuid.clone()]);
     }
@@ -1187,13 +1264,13 @@ fn move_cipher_selected(data: JsonUpcase<MoveCipherData>, headers: Headers, conn
 }
 
 #[put("/ciphers/move", data = "<data>")]
-fn move_cipher_selected_put(
+async fn move_cipher_selected_put(
     data: JsonUpcase<MoveCipherData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    move_cipher_selected(data, headers, conn, nt)
+    move_cipher_selected(data, headers, conn, nt).await
 }
 
 #[derive(FromForm)]
@@ -1203,12 +1280,12 @@ struct OrganizationId {
 }
 
 #[post("/ciphers/purge?<organization..>", data = "<data>")]
-fn delete_all(
+async fn delete_all(
     organization: Option<OrganizationId>,
     data: JsonUpcase<PasswordData>,
     headers: Headers,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let data: PasswordData = data.into_inner().data;
     let password_hash = data.MasterPasswordHash;
@@ -1222,11 +1299,11 @@ fn delete_all(
     match organization {
         Some(org_data) => {
             // Organization ID in query params, purging organization vault
-            match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &conn) {
+            match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &conn).await {
                 None => err!("You don't have permission to purge the organization vault"),
                 Some(user_org) => {
                     if user_org.atype == UserOrgType::Owner {
-                        Cipher::delete_all_by_organization(&org_data.org_id, &conn)?;
+                        Cipher::delete_all_by_organization(&org_data.org_id, &conn).await?;
                         nt.send_user_update(UpdateType::Vault, &user);
                         Ok(())
                     } else {
@@ -1238,50 +1315,56 @@ fn delete_all(
         None => {
             // No organization ID in query params, purging user vault
             // Delete ciphers and their attachments
-            for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
-                cipher.delete(&conn)?;
+            for cipher in Cipher::find_owned_by_user(&user.uuid, &conn).await {
+                cipher.delete(&conn).await?;
             }
 
             // Delete folders
-            for f in Folder::find_by_user(&user.uuid, &conn) {
-                f.delete(&conn)?;
+            for f in Folder::find_by_user(&user.uuid, &conn).await {
+                f.delete(&conn).await?;
             }
 
-            user.update_revision(&conn)?;
+            user.update_revision(&conn).await?;
             nt.send_user_update(UpdateType::Vault, &user);
             Ok(())
         }
     }
 }
 
-fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, soft_delete: bool, nt: &Notify) -> EmptyResult {
-    let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
+async fn _delete_cipher_by_uuid(
+    uuid: &str,
+    headers: &Headers,
+    conn: &DbConn,
+    soft_delete: bool,
+    nt: &Notify<'_>,
+) -> EmptyResult {
+    let mut cipher = match Cipher::find_by_uuid(uuid, conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await {
         err!("Cipher can't be deleted by user")
     }
 
     if soft_delete {
         cipher.deleted_at = Some(Utc::now().naive_utc());
-        cipher.save(conn)?;
-        nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
+        cipher.save(conn).await?;
+        nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await);
     } else {
-        cipher.delete(conn)?;
-        nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(conn));
+        cipher.delete(conn).await?;
+        nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(conn).await);
     }
 
     Ok(())
 }
 
-fn _delete_multiple_ciphers(
+async fn _delete_multiple_ciphers(
     data: JsonUpcase<Value>,
     headers: Headers,
     conn: DbConn,
     soft_delete: bool,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let data: Value = data.into_inner().data;
 
@@ -1294,7 +1377,7 @@ fn _delete_multiple_ciphers(
     };
 
     for uuid in uuids {
-        if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, soft_delete, &nt) {
+        if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, soft_delete, &nt).await {
             return error;
         };
     }
@@ -1302,24 +1385,29 @@ fn _delete_multiple_ciphers(
     Ok(())
 }
 
-fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify) -> JsonResult {
-    let mut cipher = match Cipher::find_by_uuid(uuid, conn) {
+async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify<'_>) -> JsonResult {
+    let mut cipher = match Cipher::find_by_uuid(uuid, conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await {
         err!("Cipher can't be restored by user")
     }
 
     cipher.deleted_at = None;
-    cipher.save(conn)?;
+    cipher.save(conn).await?;
 
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
-    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn)))
+    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await);
+    Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, conn).await))
 }
 
-fn _restore_multiple_ciphers(data: JsonUpcase<Value>, headers: &Headers, conn: &DbConn, nt: &Notify) -> JsonResult {
+async fn _restore_multiple_ciphers(
+    data: JsonUpcase<Value>,
+    headers: &Headers,
+    conn: &DbConn,
+    nt: &Notify<'_>,
+) -> JsonResult {
     let data: Value = data.into_inner().data;
 
     let uuids = match data.get("Ids") {
@@ -1332,7 +1420,7 @@ fn _restore_multiple_ciphers(data: JsonUpcase<Value>, headers: &Headers, conn: &
 
     let mut ciphers: Vec<Value> = Vec::new();
     for uuid in uuids {
-        match _restore_cipher_by_uuid(uuid, headers, conn, nt) {
+        match _restore_cipher_by_uuid(uuid, headers, conn, nt).await {
             Ok(json) => ciphers.push(json.into_inner()),
             err => return err,
         }
@@ -1345,14 +1433,14 @@ fn _restore_multiple_ciphers(data: JsonUpcase<Value>, headers: &Headers, conn: &
     })))
 }
 
-fn _delete_cipher_attachment_by_id(
+async fn _delete_cipher_attachment_by_id(
     uuid: &str,
     attachment_id: &str,
     headers: &Headers,
     conn: &DbConn,
-    nt: &Notify,
+    nt: &Notify<'_>,
 ) -> EmptyResult {
-    let attachment = match Attachment::find_by_id(attachment_id, conn) {
+    let attachment = match Attachment::find_by_id(attachment_id, conn).await {
         Some(attachment) => attachment,
         None => err!("Attachment doesn't exist"),
     };
@@ -1361,17 +1449,17 @@ fn _delete_cipher_attachment_by_id(
         err!("Attachment from other cipher")
     }
 
-    let cipher = match Cipher::find_by_uuid(uuid, conn) {
+    let cipher = match Cipher::find_by_uuid(uuid, conn).await {
         Some(cipher) => cipher,
         None => err!("Cipher doesn't exist"),
     };
 
-    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn) {
+    if !cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await {
         err!("Cipher cannot be deleted by user")
     }
 
     // Delete attachment
-    attachment.delete(conn)?;
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn));
+    attachment.delete(conn).await?;
+    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await);
     Ok(())
 }
diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs
index 3b6d8c08..1ac8ec98 100644
--- a/src/api/core/emergency_access.rs
+++ b/src/api/core/emergency_access.rs
@@ -11,6 +11,8 @@ use crate::{
     mail, CONFIG,
 };
 
+use futures::{stream, stream::StreamExt};
+
 pub fn routes() -> Vec<Route> {
     routes![
         get_contacts,
@@ -36,13 +38,17 @@ pub fn routes() -> Vec<Route> {
 // region get
 
 #[get("/emergency-access/trusted")]
-fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
-    let emergency_access_list = EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &conn);
-
-    let emergency_access_list_json: Vec<Value> =
-        emergency_access_list.iter().map(|e| e.to_json_grantee_details(&conn)).collect();
+    let emergency_access_list_json =
+        stream::iter(EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &conn).await)
+            .then(|e| async {
+                let e = e; // Move out this single variable
+                e.to_json_grantee_details(&conn).await
+            })
+            .collect::<Vec<Value>>()
+            .await;
 
     Ok(Json(json!({
       "Data": emergency_access_list_json,
@@ -52,13 +58,17 @@ fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult {
 }
 
 #[get("/emergency-access/granted")]
-fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
-    let emergency_access_list = EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &conn);
-
-    let emergency_access_list_json: Vec<Value> =
-        emergency_access_list.iter().map(|e| e.to_json_grantor_details(&conn)).collect();
+    let emergency_access_list_json =
+        stream::iter(EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &conn).await)
+            .then(|e| async {
+                let e = e; // Move out this single variable
+                e.to_json_grantor_details(&conn).await
+            })
+            .collect::<Vec<Value>>()
+            .await;
 
     Ok(Json(json!({
       "Data": emergency_access_list_json,
@@ -68,11 +78,11 @@ fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult {
 }
 
 #[get("/emergency-access/<emer_id>")]
-fn get_emergency_access(emer_id: String, conn: DbConn) -> JsonResult {
+async fn get_emergency_access(emer_id: String, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
-    match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
-        Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&conn))),
+    match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
+        Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&conn).await)),
         None => err!("Emergency access not valid."),
     }
 }
@@ -90,17 +100,25 @@ struct EmergencyAccessUpdateData {
 }
 
 #[put("/emergency-access/<emer_id>", data = "<data>")]
-fn put_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult {
-    post_emergency_access(emer_id, data, conn)
+async fn put_emergency_access(
+    emer_id: String,
+    data: JsonUpcase<EmergencyAccessUpdateData>,
+    conn: DbConn,
+) -> JsonResult {
+    post_emergency_access(emer_id, data, conn).await
 }
 
 #[post("/emergency-access/<emer_id>", data = "<data>")]
-fn post_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult {
+async fn post_emergency_access(
+    emer_id: String,
+    data: JsonUpcase<EmergencyAccessUpdateData>,
+    conn: DbConn,
+) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let data: EmergencyAccessUpdateData = data.into_inner().data;
 
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emergency_access) => emergency_access,
         None => err!("Emergency access not valid."),
     };
@@ -114,7 +132,7 @@ fn post_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdate
     emergency_access.wait_time_days = data.WaitTimeDays;
     emergency_access.key_encrypted = data.KeyEncrypted;
 
-    emergency_access.save(&conn)?;
+    emergency_access.save(&conn).await?;
     Ok(Json(emergency_access.to_json()))
 }
 
@@ -123,12 +141,12 @@ fn post_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdate
 // region delete
 
 #[delete("/emergency-access/<emer_id>")]
-fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
     check_emergency_access_allowed()?;
 
     let grantor_user = headers.user;
 
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => {
             if emer.grantor_uuid != grantor_user.uuid && emer.grantee_uuid != Some(grantor_user.uuid) {
                 err!("Emergency access not valid.")
@@ -137,13 +155,13 @@ fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> E
         }
         None => err!("Emergency access not valid."),
     };
-    emergency_access.delete(&conn)?;
+    emergency_access.delete(&conn).await?;
     Ok(())
 }
 
 #[post("/emergency-access/<emer_id>/delete")]
-fn post_delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
-    delete_emergency_access(emer_id, headers, conn)
+async fn post_delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
+    delete_emergency_access(emer_id, headers, conn).await
 }
 
 // endregion
@@ -159,7 +177,7 @@ struct EmergencyAccessInviteData {
 }
 
 #[post("/emergency-access/invite", data = "<data>")]
-fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, conn: DbConn) -> EmptyResult {
     check_emergency_access_allowed()?;
 
     let data: EmergencyAccessInviteData = data.into_inner().data;
@@ -180,7 +198,7 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
         err!("You can not set yourself as an emergency contact.")
     }
 
-    let grantee_user = match User::find_by_mail(&email, &conn) {
+    let grantee_user = match User::find_by_mail(&email, &conn).await {
         None => {
             if !CONFIG.invitations_allowed() {
                 err!(format!("Grantee user does not exist: {}", email))
@@ -192,11 +210,11 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
 
             if !CONFIG.mail_enabled() {
                 let invitation = Invitation::new(email.clone());
-                invitation.save(&conn)?;
+                invitation.save(&conn).await?;
             }
 
             let mut user = User::new(email.clone());
-            user.save(&conn)?;
+            user.save(&conn).await?;
             user
         }
         Some(user) => user,
@@ -208,6 +226,7 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
         &grantee_user.email,
         &conn,
     )
+    .await
     .is_some()
     {
         err!(format!("Grantee user already invited: {}", email))
@@ -220,7 +239,7 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
         new_type,
         wait_time_days,
     );
-    new_emergency_access.save(&conn)?;
+    new_emergency_access.save(&conn).await?;
 
     if CONFIG.mail_enabled() {
         mail::send_emergency_access_invite(
@@ -232,9 +251,9 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
         )?;
     } else {
         // Automatically mark user as accepted if no email invites
-        match User::find_by_mail(&email, &conn) {
+        match User::find_by_mail(&email, &conn).await {
             Some(user) => {
-                match accept_invite_process(user.uuid, new_emergency_access.uuid, Some(email), conn.borrow()) {
+                match accept_invite_process(user.uuid, new_emergency_access.uuid, Some(email), conn.borrow()).await {
                     Ok(v) => (v),
                     Err(e) => err!(e.to_string()),
                 }
@@ -247,10 +266,10 @@ fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, co
 }
 
 #[post("/emergency-access/<emer_id>/reinvite")]
-fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
     check_emergency_access_allowed()?;
 
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -268,7 +287,7 @@ fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult
         None => err!("Email not valid."),
     };
 
-    let grantee_user = match User::find_by_mail(&email, &conn) {
+    let grantee_user = match User::find_by_mail(&email, &conn).await {
         Some(user) => user,
         None => err!("Grantee user not found."),
     };
@@ -284,13 +303,15 @@ fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult
             Some(grantor_user.email),
         )?;
     } else {
-        if Invitation::find_by_mail(&email, &conn).is_none() {
+        if Invitation::find_by_mail(&email, &conn).await.is_none() {
             let invitation = Invitation::new(email);
-            invitation.save(&conn)?;
+            invitation.save(&conn).await?;
         }
 
         // Automatically mark user as accepted if no email invites
-        match accept_invite_process(grantee_user.uuid, emergency_access.uuid, emergency_access.email, conn.borrow()) {
+        match accept_invite_process(grantee_user.uuid, emergency_access.uuid, emergency_access.email, conn.borrow())
+            .await
+        {
             Ok(v) => (v),
             Err(e) => err!(e.to_string()),
         }
@@ -306,28 +327,28 @@ struct AcceptData {
 }
 
 #[post("/emergency-access/<emer_id>/accept", data = "<data>")]
-fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult {
+async fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult {
     check_emergency_access_allowed()?;
 
     let data: AcceptData = data.into_inner().data;
     let token = &data.Token;
     let claims = decode_emergency_access_invite(token)?;
 
-    let grantee_user = match User::find_by_mail(&claims.email, &conn) {
+    let grantee_user = match User::find_by_mail(&claims.email, &conn).await {
         Some(user) => {
-            Invitation::take(&claims.email, &conn);
+            Invitation::take(&claims.email, &conn).await;
             user
         }
         None => err!("Invited user not found"),
     };
 
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
 
     // get grantor user to send Accepted email
-    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
@@ -336,7 +357,7 @@ fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) ->
         && (claims.grantor_name.is_some() && grantor_user.name == claims.grantor_name.unwrap())
         && (claims.grantor_email.is_some() && grantor_user.email == claims.grantor_email.unwrap())
     {
-        match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &conn) {
+        match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &conn).await {
             Ok(v) => (v),
             Err(e) => err!(e.to_string()),
         }
@@ -351,8 +372,13 @@ fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) ->
     }
 }
 
-fn accept_invite_process(grantee_uuid: String, emer_id: String, email: Option<String>, conn: &DbConn) -> EmptyResult {
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, conn) {
+async fn accept_invite_process(
+    grantee_uuid: String,
+    emer_id: String,
+    email: Option<String>,
+    conn: &DbConn,
+) -> EmptyResult {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -369,7 +395,7 @@ fn accept_invite_process(grantee_uuid: String, emer_id: String, email: Option<St
     emergency_access.status = EmergencyAccessStatus::Accepted as i32;
     emergency_access.grantee_uuid = Some(grantee_uuid);
     emergency_access.email = None;
-    emergency_access.save(conn)
+    emergency_access.save(conn).await
 }
 
 #[derive(Deserialize)]
@@ -379,7 +405,7 @@ struct ConfirmData {
 }
 
 #[post("/emergency-access/<emer_id>/confirm", data = "<data>")]
-fn confirm_emergency_access(
+async fn confirm_emergency_access(
     emer_id: String,
     data: JsonUpcase<ConfirmData>,
     headers: Headers,
@@ -391,7 +417,7 @@ fn confirm_emergency_access(
     let data: ConfirmData = data.into_inner().data;
     let key = data.Key;
 
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -402,13 +428,13 @@ fn confirm_emergency_access(
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
 
     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
-        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
+        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await {
             Some(user) => user,
             None => err!("Grantee user not found."),
         };
@@ -417,7 +443,7 @@ fn confirm_emergency_access(
         emergency_access.key_encrypted = Some(key);
         emergency_access.email = None;
 
-        emergency_access.save(&conn)?;
+        emergency_access.save(&conn).await?;
 
         if CONFIG.mail_enabled() {
             mail::send_emergency_access_invite_confirmed(&grantee_user.email, &grantor_user.name)?;
@@ -433,11 +459,11 @@ fn confirm_emergency_access(
 // region access emergency access
 
 #[post("/emergency-access/<emer_id>/initiate")]
-fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let initiating_user = headers.user;
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -448,7 +474,7 @@ fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
@@ -458,7 +484,7 @@ fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
     emergency_access.updated_at = now;
     emergency_access.recovery_initiated_at = Some(now);
     emergency_access.last_notification_at = Some(now);
-    emergency_access.save(&conn)?;
+    emergency_access.save(&conn).await?;
 
     if CONFIG.mail_enabled() {
         mail::send_emergency_access_recovery_initiated(
@@ -472,11 +498,11 @@ fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
 }
 
 #[post("/emergency-access/<emer_id>/approve")]
-fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let approving_user = headers.user;
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -487,19 +513,19 @@ fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&approving_user.uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&approving_user.uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
 
     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
-        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
+        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await {
             Some(user) => user,
             None => err!("Grantee user not found."),
         };
 
         emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32;
-        emergency_access.save(&conn)?;
+        emergency_access.save(&conn).await?;
 
         if CONFIG.mail_enabled() {
             mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name)?;
@@ -511,11 +537,11 @@ fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
 }
 
 #[post("/emergency-access/<emer_id>/reject")]
-fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let rejecting_user = headers.user;
-    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -527,19 +553,19 @@ fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> J
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
 
     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
-        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
+        let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await {
             Some(user) => user,
             None => err!("Grantee user not found."),
         };
 
         emergency_access.status = EmergencyAccessStatus::Confirmed as i32;
-        emergency_access.save(&conn)?;
+        emergency_access.save(&conn).await?;
 
         if CONFIG.mail_enabled() {
             mail::send_emergency_access_recovery_rejected(&grantee_user.email, &grantor_user.name)?;
@@ -555,12 +581,12 @@ fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> J
 // region action
 
 #[post("/emergency-access/<emer_id>/view")]
-fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let requesting_user = headers.user;
     let host = headers.host;
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -569,10 +595,13 @@ fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> Jso
         err!("Emergency access not valid.")
     }
 
-    let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn);
-
-    let ciphers_json: Vec<Value> =
-        ciphers.iter().map(|c| c.to_json(&host, &emergency_access.grantor_uuid, &conn)).collect();
+    let ciphers_json = stream::iter(Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn).await)
+        .then(|c| async {
+            let c = c; // Move out this single variable
+            c.to_json(&host, &emergency_access.grantor_uuid, &conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     Ok(Json(json!({
       "Ciphers": ciphers_json,
@@ -582,11 +611,11 @@ fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> Jso
 }
 
 #[post("/emergency-access/<emer_id>/takeover")]
-fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     check_emergency_access_allowed()?;
 
     let requesting_user = headers.user;
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -595,7 +624,7 @@ fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
@@ -616,7 +645,7 @@ struct EmergencyAccessPasswordData {
 }
 
 #[post("/emergency-access/<emer_id>/password", data = "<data>")]
-fn password_emergency_access(
+async fn password_emergency_access(
     emer_id: String,
     data: JsonUpcase<EmergencyAccessPasswordData>,
     headers: Headers,
@@ -629,7 +658,7 @@ fn password_emergency_access(
     let key = data.Key;
 
     let requesting_user = headers.user;
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -638,7 +667,7 @@ fn password_emergency_access(
         err!("Emergency access not valid.")
     }
 
-    let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
+    let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
@@ -646,18 +675,15 @@ fn password_emergency_access(
     // change grantor_user password
     grantor_user.set_password(new_master_password_hash, None);
     grantor_user.akey = key;
-    grantor_user.save(&conn)?;
+    grantor_user.save(&conn).await?;
 
     // Disable TwoFactor providers since they will otherwise block logins
-    TwoFactor::delete_all_by_user(&grantor_user.uuid, &conn)?;
-
-    // Removing owner, check that there are at least another owner
-    let user_org_grantor = UserOrganization::find_any_state_by_user(&grantor_user.uuid, &conn);
+    TwoFactor::delete_all_by_user(&grantor_user.uuid, &conn).await?;
 
     // Remove grantor from all organisations unless Owner
-    for user_org in user_org_grantor {
+    for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &conn).await {
         if user_org.atype != UserOrgType::Owner as i32 {
-            user_org.delete(&conn)?;
+            user_org.delete(&conn).await?;
         }
     }
     Ok(())
@@ -666,9 +692,9 @@ fn password_emergency_access(
 // endregion
 
 #[get("/emergency-access/<emer_id>/policies")]
-fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
+async fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
     let requesting_user = headers.user;
-    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
+    let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await {
         Some(emer) => emer,
         None => err!("Emergency access not valid."),
     };
@@ -677,13 +703,13 @@ fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) ->
         err!("Emergency access not valid.")
     }
 
-    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
+    let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await {
         Some(user) => user,
         None => err!("Grantor user not found."),
     };
 
     let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &conn);
-    let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
+    let policies_json: Vec<Value> = policies.await.iter().map(OrgPolicy::to_json).collect();
 
     Ok(Json(json!({
         "Data": policies_json,
@@ -716,7 +742,7 @@ pub async fn emergency_request_timeout_job(pool: DbPool) {
     }
 
     if let Ok(conn) = pool.get().await {
-        let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn);
+        let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn).await;
 
         if emergency_access_list.is_empty() {
             debug!("No emergency request timeout to approve");
@@ -728,15 +754,17 @@ pub async fn emergency_request_timeout_job(pool: DbPool) {
                     >= emer.recovery_initiated_at.unwrap() + Duration::days(emer.wait_time_days as i64)
             {
                 emer.status = EmergencyAccessStatus::RecoveryApproved as i32;
-                emer.save(&conn).expect("Cannot save emergency access on job");
+                emer.save(&conn).await.expect("Cannot save emergency access on job");
 
                 if CONFIG.mail_enabled() {
                     // get grantor user to send Accepted email
-                    let grantor_user = User::find_by_uuid(&emer.grantor_uuid, &conn).expect("Grantor user not found.");
+                    let grantor_user =
+                        User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found.");
 
                     // get grantee user to send Accepted email
                     let grantee_user =
                         User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &conn)
+                            .await
                             .expect("Grantee user not found.");
 
                     mail::send_emergency_access_recovery_timed_out(
@@ -763,7 +791,7 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) {
     }
 
     if let Ok(conn) = pool.get().await {
-        let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn);
+        let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn).await;
 
         if emergency_access_list.is_empty() {
             debug!("No emergency request reminder notification to send");
@@ -777,15 +805,17 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) {
                     || (emer.last_notification_at.is_some()
                         && Utc::now().naive_utc() >= emer.last_notification_at.unwrap() + Duration::days(1)))
             {
-                emer.save(&conn).expect("Cannot save emergency access on job");
+                emer.save(&conn).await.expect("Cannot save emergency access on job");
 
                 if CONFIG.mail_enabled() {
                     // get grantor user to send Accepted email
-                    let grantor_user = User::find_by_uuid(&emer.grantor_uuid, &conn).expect("Grantor user not found.");
+                    let grantor_user =
+                        User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found.");
 
                     // get grantee user to send Accepted email
                     let grantee_user =
                         User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &conn)
+                            .await
                             .expect("Grantee user not found.");
 
                     mail::send_emergency_access_recovery_reminder(
diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs
index e2a32f7b..3b6d2c3a 100644
--- a/src/api/core/folders.rs
+++ b/src/api/core/folders.rs
@@ -12,9 +12,8 @@ pub fn routes() -> Vec<rocket::Route> {
 }
 
 #[get("/folders")]
-fn get_folders(headers: Headers, conn: DbConn) -> Json<Value> {
-    let folders = Folder::find_by_user(&headers.user.uuid, &conn);
-
+async fn get_folders(headers: Headers, conn: DbConn) -> Json<Value> {
+    let folders = Folder::find_by_user(&headers.user.uuid, &conn).await;
     let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
 
     Json(json!({
@@ -25,8 +24,8 @@ fn get_folders(headers: Headers, conn: DbConn) -> Json<Value> {
 }
 
 #[get("/folders/<uuid>")]
-fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
-    let folder = match Folder::find_by_uuid(&uuid, &conn) {
+async fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
+    let folder = match Folder::find_by_uuid(&uuid, &conn).await {
         Some(folder) => folder,
         _ => err!("Invalid folder"),
     };
@@ -45,27 +44,39 @@ pub struct FolderData {
 }
 
 #[post("/folders", data = "<data>")]
-fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
+async fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
     let data: FolderData = data.into_inner().data;
 
     let mut folder = Folder::new(headers.user.uuid, data.Name);
 
-    folder.save(&conn)?;
+    folder.save(&conn).await?;
     nt.send_folder_update(UpdateType::FolderCreate, &folder);
 
     Ok(Json(folder.to_json()))
 }
 
 #[post("/folders/<uuid>", data = "<data>")]
-fn post_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    put_folder(uuid, data, headers, conn, nt)
+async fn post_folder(
+    uuid: String,
+    data: JsonUpcase<FolderData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    put_folder(uuid, data, headers, conn, nt).await
 }
 
 #[put("/folders/<uuid>", data = "<data>")]
-fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
+async fn put_folder(
+    uuid: String,
+    data: JsonUpcase<FolderData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
     let data: FolderData = data.into_inner().data;
 
-    let mut folder = match Folder::find_by_uuid(&uuid, &conn) {
+    let mut folder = match Folder::find_by_uuid(&uuid, &conn).await {
         Some(folder) => folder,
         _ => err!("Invalid folder"),
     };
@@ -76,20 +87,20 @@ fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn
 
     folder.name = data.Name;
 
-    folder.save(&conn)?;
+    folder.save(&conn).await?;
     nt.send_folder_update(UpdateType::FolderUpdate, &folder);
 
     Ok(Json(folder.to_json()))
 }
 
 #[post("/folders/<uuid>/delete")]
-fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    delete_folder(uuid, headers, conn, nt)
+async fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    delete_folder(uuid, headers, conn, nt).await
 }
 
 #[delete("/folders/<uuid>")]
-fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    let folder = match Folder::find_by_uuid(&uuid, &conn) {
+async fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    let folder = match Folder::find_by_uuid(&uuid, &conn).await {
         Some(folder) => folder,
         _ => err!("Invalid folder"),
     };
@@ -99,7 +110,7 @@ fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify) -> Em
     }
 
     // Delete the actual folder entry
-    folder.delete(&conn)?;
+    folder.delete(&conn).await?;
 
     nt.send_folder_update(UpdateType::FolderDelete, &folder);
     Ok(())
diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs
index 79556494..e34343d3 100644
--- a/src/api/core/mod.rs
+++ b/src/api/core/mod.rs
@@ -121,7 +121,7 @@ struct EquivDomainData {
 }
 
 #[post("/settings/domains", data = "<data>")]
-fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: EquivDomainData = data.into_inner().data;
 
     let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
@@ -133,14 +133,14 @@ fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: Db
     user.excluded_globals = to_string(&excluded_globals).unwrap_or_else(|_| "[]".to_string());
     user.equivalent_domains = to_string(&equivalent_domains).unwrap_or_else(|_| "[]".to_string());
 
-    user.save(&conn)?;
+    user.save(&conn).await?;
 
     Ok(Json(json!({})))
 }
 
 #[put("/settings/domains", data = "<data>")]
-fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
-    post_eq_domains(data, headers, conn)
+async fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
+    post_eq_domains(data, headers, conn).await
 }
 
 #[get("/hibp/breach?<username>")]
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
index 5716cbf0..bb6c6634 100644
--- a/src/api/core/organizations.rs
+++ b/src/api/core/organizations.rs
@@ -10,6 +10,8 @@ use crate::{
     mail, CONFIG,
 };
 
+use futures::{stream, stream::StreamExt};
+
 pub fn routes() -> Vec<Route> {
     routes![
         get_organization,
@@ -98,11 +100,11 @@ struct OrgBulkIds {
 }
 
 #[post("/organizations", data = "<data>")]
-fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn) -> JsonResult {
+async fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn) -> JsonResult {
     if !CONFIG.is_org_creation_allowed(&headers.user.email) {
         err!("User not allowed to create organizations")
     }
-    if OrgPolicy::is_applicable_to_user(&headers.user.uuid, OrgPolicyType::SingleOrg, &conn) {
+    if OrgPolicy::is_applicable_to_user(&headers.user.uuid, OrgPolicyType::SingleOrg, &conn).await {
         err!(
             "You may not create an organization. You belong to an organization which has a policy that prohibits you from being a member of any other organization."
         )
@@ -125,15 +127,15 @@ fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn
     user_org.atype = UserOrgType::Owner as i32;
     user_org.status = UserOrgStatus::Confirmed as i32;
 
-    org.save(&conn)?;
-    user_org.save(&conn)?;
-    collection.save(&conn)?;
+    org.save(&conn).await?;
+    user_org.save(&conn).await?;
+    collection.save(&conn).await?;
 
     Ok(Json(org.to_json()))
 }
 
 #[delete("/organizations/<org_id>", data = "<data>")]
-fn delete_organization(
+async fn delete_organization(
     org_id: String,
     data: JsonUpcase<PasswordData>,
     headers: OwnerHeaders,
@@ -146,61 +148,61 @@ fn delete_organization(
         err!("Invalid password")
     }
 
-    match Organization::find_by_uuid(&org_id, &conn) {
+    match Organization::find_by_uuid(&org_id, &conn).await {
         None => err!("Organization not found"),
-        Some(org) => org.delete(&conn),
+        Some(org) => org.delete(&conn).await,
     }
 }
 
 #[post("/organizations/<org_id>/delete", data = "<data>")]
-fn post_delete_organization(
+async fn post_delete_organization(
     org_id: String,
     data: JsonUpcase<PasswordData>,
     headers: OwnerHeaders,
     conn: DbConn,
 ) -> EmptyResult {
-    delete_organization(org_id, data, headers, conn)
+    delete_organization(org_id, data, headers, conn).await
 }
 
 #[post("/organizations/<org_id>/leave")]
-fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
-    match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
+async fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
+    match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await {
         None => err!("User not part of organization"),
         Some(user_org) => {
             if user_org.atype == UserOrgType::Owner {
                 let num_owners =
-                    UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).len();
+                    UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).await.len();
 
                 if num_owners <= 1 {
                     err!("The last owner can't leave")
                 }
             }
 
-            user_org.delete(&conn)
+            user_org.delete(&conn).await
         }
     }
 }
 
 #[get("/organizations/<org_id>")]
-fn get_organization(org_id: String, _headers: OwnerHeaders, conn: DbConn) -> JsonResult {
-    match Organization::find_by_uuid(&org_id, &conn) {
+async fn get_organization(org_id: String, _headers: OwnerHeaders, conn: DbConn) -> JsonResult {
+    match Organization::find_by_uuid(&org_id, &conn).await {
         Some(organization) => Ok(Json(organization.to_json())),
         None => err!("Can't find organization details"),
     }
 }
 
 #[put("/organizations/<org_id>", data = "<data>")]
-fn put_organization(
+async fn put_organization(
     org_id: String,
     headers: OwnerHeaders,
     data: JsonUpcase<OrganizationUpdateData>,
     conn: DbConn,
 ) -> JsonResult {
-    post_organization(org_id, headers, data, conn)
+    post_organization(org_id, headers, data, conn).await
 }
 
 #[post("/organizations/<org_id>", data = "<data>")]
-fn post_organization(
+async fn post_organization(
     org_id: String,
     _headers: OwnerHeaders,
     data: JsonUpcase<OrganizationUpdateData>,
@@ -208,7 +210,7 @@ fn post_organization(
 ) -> JsonResult {
     let data: OrganizationUpdateData = data.into_inner().data;
 
-    let mut org = match Organization::find_by_uuid(&org_id, &conn) {
+    let mut org = match Organization::find_by_uuid(&org_id, &conn).await {
         Some(organization) => organization,
         None => err!("Can't find organization details"),
     };
@@ -216,16 +218,16 @@ fn post_organization(
     org.name = data.Name;
     org.billing_email = data.BillingEmail;
 
-    org.save(&conn)?;
+    org.save(&conn).await?;
     Ok(Json(org.to_json()))
 }
 
 // GET /api/collections?writeOnly=false
 #[get("/collections")]
-fn get_user_collections(headers: Headers, conn: DbConn) -> Json<Value> {
+async fn get_user_collections(headers: Headers, conn: DbConn) -> Json<Value> {
     Json(json!({
         "Data":
-            Collection::find_by_user_uuid(&headers.user.uuid, &conn)
+            Collection::find_by_user_uuid(&headers.user.uuid, &conn).await
             .iter()
             .map(Collection::to_json)
             .collect::<Value>(),
@@ -235,10 +237,10 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> Json<Value> {
 }
 
 #[get("/organizations/<org_id>/collections")]
-fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json<Value> {
+async fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json<Value> {
     Json(json!({
         "Data":
-            Collection::find_by_organization(&org_id, &conn)
+            Collection::find_by_organization(&org_id, &conn).await
             .iter()
             .map(Collection::to_json)
             .collect::<Value>(),
@@ -248,7 +250,7 @@ fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, conn: DbCo
 }
 
 #[post("/organizations/<org_id>/collections", data = "<data>")]
-fn post_organization_collections(
+async fn post_organization_collections(
     org_id: String,
     headers: ManagerHeadersLoose,
     data: JsonUpcase<NewCollectionData>,
@@ -256,43 +258,43 @@ fn post_organization_collections(
 ) -> JsonResult {
     let data: NewCollectionData = data.into_inner().data;
 
-    let org = match Organization::find_by_uuid(&org_id, &conn) {
+    let org = match Organization::find_by_uuid(&org_id, &conn).await {
         Some(organization) => organization,
         None => err!("Can't find organization details"),
     };
 
     // Get the user_organization record so that we can check if the user has access to all collections.
-    let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
+    let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await {
         Some(u) => u,
         None => err!("User is not part of organization"),
     };
 
     let collection = Collection::new(org.uuid, data.Name);
-    collection.save(&conn)?;
+    collection.save(&conn).await?;
 
     // If the user doesn't have access to all collections, only in case of a Manger,
     // then we need to save the creating user uuid (Manager) to the users_collection table.
     // Else the user will not have access to his own created collection.
     if !user_org.access_all {
-        CollectionUser::save(&headers.user.uuid, &collection.uuid, false, false, &conn)?;
+        CollectionUser::save(&headers.user.uuid, &collection.uuid, false, false, &conn).await?;
     }
 
     Ok(Json(collection.to_json()))
 }
 
 #[put("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
-fn put_organization_collection_update(
+async fn put_organization_collection_update(
     org_id: String,
     col_id: String,
     headers: ManagerHeaders,
     data: JsonUpcase<NewCollectionData>,
     conn: DbConn,
 ) -> JsonResult {
-    post_organization_collection_update(org_id, col_id, headers, data, conn)
+    post_organization_collection_update(org_id, col_id, headers, data, conn).await
 }
 
 #[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
-fn post_organization_collection_update(
+async fn post_organization_collection_update(
     org_id: String,
     col_id: String,
     _headers: ManagerHeaders,
@@ -301,12 +303,12 @@ fn post_organization_collection_update(
 ) -> JsonResult {
     let data: NewCollectionData = data.into_inner().data;
 
-    let org = match Organization::find_by_uuid(&org_id, &conn) {
+    let org = match Organization::find_by_uuid(&org_id, &conn).await {
         Some(organization) => organization,
         None => err!("Can't find organization details"),
     };
 
-    let mut collection = match Collection::find_by_uuid(&col_id, &conn) {
+    let mut collection = match Collection::find_by_uuid(&col_id, &conn).await {
         Some(collection) => collection,
         None => err!("Collection not found"),
     };
@@ -316,20 +318,20 @@ fn post_organization_collection_update(
     }
 
     collection.name = data.Name;
-    collection.save(&conn)?;
+    collection.save(&conn).await?;
 
     Ok(Json(collection.to_json()))
 }
 
 #[delete("/organizations/<org_id>/collections/<col_id>/user/<org_user_id>")]
-fn delete_organization_collection_user(
+async fn delete_organization_collection_user(
     org_id: String,
     col_id: String,
     org_user_id: String,
     _headers: AdminHeaders,
     conn: DbConn,
 ) -> EmptyResult {
-    let collection = match Collection::find_by_uuid(&col_id, &conn) {
+    let collection = match Collection::find_by_uuid(&col_id, &conn).await {
         None => err!("Collection not found"),
         Some(collection) => {
             if collection.org_uuid == org_id {
@@ -340,40 +342,40 @@ fn delete_organization_collection_user(
         }
     };
 
-    match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
+    match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn).await {
         None => err!("User not found in organization"),
         Some(user_org) => {
-            match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn) {
+            match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn).await {
                 None => err!("User not assigned to collection"),
-                Some(col_user) => col_user.delete(&conn),
+                Some(col_user) => col_user.delete(&conn).await,
             }
         }
     }
 }
 
 #[post("/organizations/<org_id>/collections/<col_id>/delete-user/<org_user_id>")]
-fn post_organization_collection_delete_user(
+async fn post_organization_collection_delete_user(
     org_id: String,
     col_id: String,
     org_user_id: String,
     headers: AdminHeaders,
     conn: DbConn,
 ) -> EmptyResult {
-    delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn)
+    delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn).await
 }
 
 #[delete("/organizations/<org_id>/collections/<col_id>")]
-fn delete_organization_collection(
+async fn delete_organization_collection(
     org_id: String,
     col_id: String,
     _headers: ManagerHeaders,
     conn: DbConn,
 ) -> EmptyResult {
-    match Collection::find_by_uuid(&col_id, &conn) {
+    match Collection::find_by_uuid(&col_id, &conn).await {
         None => err!("Collection not found"),
         Some(collection) => {
             if collection.org_uuid == org_id {
-                collection.delete(&conn)
+                collection.delete(&conn).await
             } else {
                 err!("Collection and Organization id do not match")
             }
@@ -389,19 +391,24 @@ struct DeleteCollectionData {
 }
 
 #[post("/organizations/<org_id>/collections/<col_id>/delete", data = "<_data>")]
-fn post_organization_collection_delete(
+async fn post_organization_collection_delete(
     org_id: String,
     col_id: String,
     headers: ManagerHeaders,
     _data: JsonUpcase<DeleteCollectionData>,
     conn: DbConn,
 ) -> EmptyResult {
-    delete_organization_collection(org_id, col_id, headers, conn)
+    delete_organization_collection(org_id, col_id, headers, conn).await
 }
 
 #[get("/organizations/<org_id>/collections/<coll_id>/details")]
-fn get_org_collection_detail(org_id: String, coll_id: String, headers: ManagerHeaders, conn: DbConn) -> JsonResult {
-    match Collection::find_by_uuid_and_user(&coll_id, &headers.user.uuid, &conn) {
+async fn get_org_collection_detail(
+    org_id: String,
+    coll_id: String,
+    headers: ManagerHeaders,
+    conn: DbConn,
+) -> JsonResult {
+    match Collection::find_by_uuid_and_user(&coll_id, &headers.user.uuid, &conn).await {
         None => err!("Collection not found"),
         Some(collection) => {
             if collection.org_uuid != org_id {
@@ -414,28 +421,29 @@ fn get_org_collection_detail(org_id: String, coll_id: String, headers: ManagerHe
 }
 
 #[get("/organizations/<org_id>/collections/<coll_id>/users")]
-fn get_collection_users(org_id: String, coll_id: String, _headers: ManagerHeaders, conn: DbConn) -> JsonResult {
+async fn get_collection_users(org_id: String, coll_id: String, _headers: ManagerHeaders, conn: DbConn) -> JsonResult {
     // Get org and collection, check that collection is from org
-    let collection = match Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn) {
+    let collection = match Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn).await {
         None => err!("Collection not found in Organization"),
         Some(collection) => collection,
     };
 
-    // Get the users from collection
-    let user_list: Vec<Value> = CollectionUser::find_by_collection(&collection.uuid, &conn)
-        .iter()
-        .map(|col_user| {
+    let user_list = stream::iter(CollectionUser::find_by_collection(&collection.uuid, &conn).await)
+        .then(|col_user| async {
+            let col_user = col_user; // Move out this single variable
             UserOrganization::find_by_user_and_org(&col_user.user_uuid, &org_id, &conn)
+                .await
                 .unwrap()
-                .to_json_user_access_restrictions(col_user)
+                .to_json_user_access_restrictions(&col_user)
         })
-        .collect();
+        .collect::<Vec<Value>>()
+        .await;
 
     Ok(Json(json!(user_list)))
 }
 
 #[put("/organizations/<org_id>/collections/<coll_id>/users", data = "<data>")]
-fn put_collection_users(
+async fn put_collection_users(
     org_id: String,
     coll_id: String,
     data: JsonUpcaseVec<CollectionData>,
@@ -443,16 +451,16 @@ fn put_collection_users(
     conn: DbConn,
 ) -> EmptyResult {
     // Get org and collection, check that collection is from org
-    if Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn).is_none() {
+    if Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn).await.is_none() {
         err!("Collection not found in Organization")
     }
 
     // Delete all the user-collections
-    CollectionUser::delete_all_by_collection(&coll_id, &conn)?;
+    CollectionUser::delete_all_by_collection(&coll_id, &conn).await?;
 
     // And then add all the received ones (except if the user has access_all)
     for d in data.iter().map(|d| &d.data) {
-        let user = match UserOrganization::find_by_uuid(&d.Id, &conn) {
+        let user = match UserOrganization::find_by_uuid(&d.Id, &conn).await {
             Some(u) => u,
             None => err!("User is not part of organization"),
         };
@@ -461,7 +469,7 @@ fn put_collection_users(
             continue;
         }
 
-        CollectionUser::save(&user.user_uuid, &coll_id, d.ReadOnly, d.HidePasswords, &conn)?;
+        CollectionUser::save(&user.user_uuid, &coll_id, d.ReadOnly, d.HidePasswords, &conn).await?;
     }
 
     Ok(())
@@ -474,10 +482,14 @@ struct OrgIdData {
 }
 
 #[get("/ciphers/organization-details?<data..>")]
-fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json<Value> {
-    let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
-    let ciphers_json: Vec<Value> =
-        ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
+async fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json<Value> {
+    let ciphers_json = stream::iter(Cipher::find_by_org(&data.organization_id, &conn).await)
+        .then(|c| async {
+            let c = c; // Move out this single variable
+            c.to_json(&headers.host, &headers.user.uuid, &conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     Json(json!({
       "Data": ciphers_json,
@@ -487,9 +499,14 @@ fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json<Valu
 }
 
 #[get("/organizations/<org_id>/users")]
-fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json<Value> {
-    let users = UserOrganization::find_by_org(&org_id, &conn);
-    let users_json: Vec<Value> = users.iter().map(|c| c.to_json_user_details(&conn)).collect();
+async fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json<Value> {
+    let users_json = stream::iter(UserOrganization::find_by_org(&org_id, &conn).await)
+        .then(|u| async {
+            let u = u; // Move out this single variable
+            u.to_json_user_details(&conn).await
+        })
+        .collect::<Vec<Value>>()
+        .await;
 
     Json(json!({
         "Data": users_json,
@@ -499,10 +516,15 @@ fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) ->
 }
 
 #[post("/organizations/<org_id>/keys", data = "<data>")]
-fn post_org_keys(org_id: String, data: JsonUpcase<OrgKeyData>, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
+async fn post_org_keys(
+    org_id: String,
+    data: JsonUpcase<OrgKeyData>,
+    _headers: AdminHeaders,
+    conn: DbConn,
+) -> JsonResult {
     let data: OrgKeyData = data.into_inner().data;
 
-    let mut org = match Organization::find_by_uuid(&org_id, &conn) {
+    let mut org = match Organization::find_by_uuid(&org_id, &conn).await {
         Some(organization) => {
             if organization.private_key.is_some() && organization.public_key.is_some() {
                 err!("Organization Keys already exist")
@@ -515,7 +537,7 @@ fn post_org_keys(org_id: String, data: JsonUpcase<OrgKeyData>, _headers: AdminHe
     org.private_key = Some(data.EncryptedPrivateKey);
     org.public_key = Some(data.PublicKey);
 
-    org.save(&conn)?;
+    org.save(&conn).await?;
 
     Ok(Json(json!({
         "Object": "organizationKeys",
@@ -542,7 +564,7 @@ struct InviteData {
 }
 
 #[post("/organizations/<org_id>/users/invite", data = "<data>")]
-fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
+async fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
     let data: InviteData = data.into_inner().data;
 
     let new_type = match UserOrgType::from_str(&data.Type.into_string()) {
@@ -561,7 +583,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
         } else {
             UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites
         };
-        let user = match User::find_by_mail(&email, &conn) {
+        let user = match User::find_by_mail(&email, &conn).await {
             None => {
                 if !CONFIG.invitations_allowed() {
                     err!(format!("User does not exist: {}", email))
@@ -573,16 +595,16 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
 
                 if !CONFIG.mail_enabled() {
                     let invitation = Invitation::new(email.clone());
-                    invitation.save(&conn)?;
+                    invitation.save(&conn).await?;
                 }
 
                 let mut user = User::new(email.clone());
-                user.save(&conn)?;
+                user.save(&conn).await?;
                 user_org_status = UserOrgStatus::Invited as i32;
                 user
             }
             Some(user) => {
-                if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).is_some() {
+                if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).await.is_some() {
                     err!(format!("User already in organization: {}", email))
                 } else {
                     user
@@ -599,19 +621,20 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
         // If no accessAll, add the collections received
         if !access_all {
             for col in data.Collections.iter().flatten() {
-                match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
+                match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn).await {
                     None => err!("Collection not found in Organization"),
                     Some(collection) => {
-                        CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &conn)?;
+                        CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &conn)
+                            .await?;
                     }
                 }
             }
         }
 
-        new_user.save(&conn)?;
+        new_user.save(&conn).await?;
 
         if CONFIG.mail_enabled() {
-            let org_name = match Organization::find_by_uuid(&org_id, &conn) {
+            let org_name = match Organization::find_by_uuid(&org_id, &conn).await {
                 Some(org) => org.name,
                 None => err!("Error looking up organization"),
             };
@@ -631,7 +654,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
 }
 
 #[post("/organizations/<org_id>/users/reinvite", data = "<data>")]
-fn bulk_reinvite_user(
+async fn bulk_reinvite_user(
     org_id: String,
     data: JsonUpcase<OrgBulkIds>,
     headers: AdminHeaders,
@@ -641,7 +664,7 @@ fn bulk_reinvite_user(
 
     let mut bulk_response = Vec::new();
     for org_user_id in data.Ids {
-        let err_msg = match _reinvite_user(&org_id, &org_user_id, &headers.user.email, &conn) {
+        let err_msg = match _reinvite_user(&org_id, &org_user_id, &headers.user.email, &conn).await {
             Ok(_) => String::from(""),
             Err(e) => format!("{:?}", e),
         };
@@ -663,11 +686,11 @@ fn bulk_reinvite_user(
 }
 
 #[post("/organizations/<org_id>/users/<user_org>/reinvite")]
-fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
-    _reinvite_user(&org_id, &user_org, &headers.user.email, &conn)
+async fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
+    _reinvite_user(&org_id, &user_org, &headers.user.email, &conn).await
 }
 
-fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &DbConn) -> EmptyResult {
+async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &DbConn) -> EmptyResult {
     if !CONFIG.invitations_allowed() {
         err!("Invitations are not allowed.")
     }
@@ -676,7 +699,7 @@ fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &D
         err!("SMTP is not configured.")
     }
 
-    let user_org = match UserOrganization::find_by_uuid(user_org, conn) {
+    let user_org = match UserOrganization::find_by_uuid(user_org, conn).await {
         Some(user_org) => user_org,
         None => err!("The user hasn't been invited to the organization."),
     };
@@ -685,12 +708,12 @@ fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &D
         err!("The user is already accepted or confirmed to the organization")
     }
 
-    let user = match User::find_by_uuid(&user_org.user_uuid, conn) {
+    let user = match User::find_by_uuid(&user_org.user_uuid, conn).await {
         Some(user) => user,
         None => err!("User not found."),
     };
 
-    let org_name = match Organization::find_by_uuid(org_id, conn) {
+    let org_name = match Organization::find_by_uuid(org_id, conn).await {
         Some(org) => org.name,
         None => err!("Error looking up organization."),
     };
@@ -706,7 +729,7 @@ fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &D
         )?;
     } else {
         let invitation = Invitation::new(user.email);
-        invitation.save(conn)?;
+        invitation.save(conn).await?;
     }
 
     Ok(())
@@ -719,18 +742,23 @@ struct AcceptData {
 }
 
 #[post("/organizations/<_org_id>/users/<_org_user_id>/accept", data = "<data>")]
-fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult {
+async fn accept_invite(
+    _org_id: String,
+    _org_user_id: String,
+    data: JsonUpcase<AcceptData>,
+    conn: DbConn,
+) -> EmptyResult {
     // The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead
     let data: AcceptData = data.into_inner().data;
     let token = &data.Token;
     let claims = decode_invite(token)?;
 
-    match User::find_by_mail(&claims.email, &conn) {
+    match User::find_by_mail(&claims.email, &conn).await {
         Some(_) => {
-            Invitation::take(&claims.email, &conn);
+            Invitation::take(&claims.email, &conn).await;
 
             if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) {
-                let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &conn) {
+                let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &conn).await {
                     Some(user_org) => user_org,
                     None => err!("Error accepting the invitation"),
                 };
@@ -739,11 +767,11 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
                     err!("User already accepted the invitation")
                 }
 
-                let user_twofactor_disabled = TwoFactor::find_by_user(&user_org.user_uuid, &conn).is_empty();
+                let user_twofactor_disabled = TwoFactor::find_by_user(&user_org.user_uuid, &conn).await.is_empty();
 
                 let policy = OrgPolicyType::TwoFactorAuthentication as i32;
                 let org_twofactor_policy_enabled =
-                    match OrgPolicy::find_by_org_and_type(&user_org.org_uuid, policy, &conn) {
+                    match OrgPolicy::find_by_org_and_type(&user_org.org_uuid, policy, &conn).await {
                         Some(p) => p.enabled,
                         None => false,
                     };
@@ -754,12 +782,15 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
 
                 // Enforce Single Organization Policy of organization user is trying to join
                 let single_org_policy_enabled =
-                    match OrgPolicy::find_by_org_and_type(&user_org.org_uuid, OrgPolicyType::SingleOrg as i32, &conn) {
+                    match OrgPolicy::find_by_org_and_type(&user_org.org_uuid, OrgPolicyType::SingleOrg as i32, &conn)
+                        .await
+                    {
                         Some(p) => p.enabled,
                         None => false,
                     };
                 if single_org_policy_enabled && user_org.atype < UserOrgType::Admin {
                     let is_member_of_another_org = UserOrganization::find_any_state_by_user(&user_org.user_uuid, &conn)
+                        .await
                         .into_iter()
                         .filter(|uo| uo.org_uuid != user_org.org_uuid)
                         .count()
@@ -770,14 +801,14 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
                 }
 
                 // Enforce Single Organization Policy of other organizations user is a member of
-                if OrgPolicy::is_applicable_to_user(&user_org.user_uuid, OrgPolicyType::SingleOrg, &conn) {
+                if OrgPolicy::is_applicable_to_user(&user_org.user_uuid, OrgPolicyType::SingleOrg, &conn).await {
                     err!(
                         "You cannot join this organization because you are a member of an organization which forbids it"
                     )
                 }
 
                 user_org.status = UserOrgStatus::Accepted as i32;
-                user_org.save(&conn)?;
+                user_org.save(&conn).await?;
             }
         }
         None => err!("Invited user not found"),
@@ -786,7 +817,7 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
     if CONFIG.mail_enabled() {
         let mut org_name = CONFIG.invitation_org_name();
         if let Some(org_id) = &claims.org_id {
-            org_name = match Organization::find_by_uuid(org_id, &conn) {
+            org_name = match Organization::find_by_uuid(org_id, &conn).await {
                 Some(org) => org.name,
                 None => err!("Organization not found."),
             };
@@ -804,7 +835,12 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
 }
 
 #[post("/organizations/<org_id>/users/confirm", data = "<data>")]
-fn bulk_confirm_invite(org_id: String, data: JsonUpcase<Value>, headers: AdminHeaders, conn: DbConn) -> Json<Value> {
+async fn bulk_confirm_invite(
+    org_id: String,
+    data: JsonUpcase<Value>,
+    headers: AdminHeaders,
+    conn: DbConn,
+) -> Json<Value> {
     let data = data.into_inner().data;
 
     let mut bulk_response = Vec::new();
@@ -813,7 +849,7 @@ fn bulk_confirm_invite(org_id: String, data: JsonUpcase<Value>, headers: AdminHe
             for invite in keys {
                 let org_user_id = invite["Id"].as_str().unwrap_or_default();
                 let user_key = invite["Key"].as_str().unwrap_or_default();
-                let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &conn) {
+                let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &conn).await {
                     Ok(_) => String::from(""),
                     Err(e) => format!("{:?}", e),
                 };
@@ -838,7 +874,7 @@ fn bulk_confirm_invite(org_id: String, data: JsonUpcase<Value>, headers: AdminHe
 }
 
 #[post("/organizations/<org_id>/users/<org_user_id>/confirm", data = "<data>")]
-fn confirm_invite(
+async fn confirm_invite(
     org_id: String,
     org_user_id: String,
     data: JsonUpcase<Value>,
@@ -847,15 +883,21 @@ fn confirm_invite(
 ) -> EmptyResult {
     let data = data.into_inner().data;
     let user_key = data["Key"].as_str().unwrap_or_default();
-    _confirm_invite(&org_id, &org_user_id, user_key, &headers, &conn)
+    _confirm_invite(&org_id, &org_user_id, user_key, &headers, &conn).await
 }
 
-fn _confirm_invite(org_id: &str, org_user_id: &str, key: &str, headers: &AdminHeaders, conn: &DbConn) -> EmptyResult {
+async fn _confirm_invite(
+    org_id: &str,
+    org_user_id: &str,
+    key: &str,
+    headers: &AdminHeaders,
+    conn: &DbConn,
+) -> EmptyResult {
     if key.is_empty() || org_user_id.is_empty() {
         err!("Key or UserId is not set, unable to process request");
     }
 
-    let mut user_to_confirm = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn) {
+    let mut user_to_confirm = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
         Some(user) => user,
         None => err!("The specified user isn't a member of the organization"),
     };
@@ -872,28 +914,28 @@ fn _confirm_invite(org_id: &str, org_user_id: &str, key: &str, headers: &AdminHe
     user_to_confirm.akey = key.to_string();
 
     if CONFIG.mail_enabled() {
-        let org_name = match Organization::find_by_uuid(org_id, conn) {
+        let org_name = match Organization::find_by_uuid(org_id, conn).await {
             Some(org) => org.name,
             None => err!("Error looking up organization."),
         };
-        let address = match User::find_by_uuid(&user_to_confirm.user_uuid, conn) {
+        let address = match User::find_by_uuid(&user_to_confirm.user_uuid, conn).await {
             Some(user) => user.email,
             None => err!("Error looking up user."),
         };
         mail::send_invite_confirmed(&address, &org_name)?;
     }
 
-    user_to_confirm.save(conn)
+    user_to_confirm.save(conn).await
 }
 
 #[get("/organizations/<org_id>/users/<org_user_id>")]
-fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
-    let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
+async fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
+    let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn).await {
         Some(user) => user,
         None => err!("The specified user isn't a member of the organization"),
     };
 
-    Ok(Json(user.to_json_details(&conn)))
+    Ok(Json(user.to_json_details(&conn).await))
 }
 
 #[derive(Deserialize)]
@@ -905,18 +947,18 @@ struct EditUserData {
 }
 
 #[put("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
-fn put_organization_user(
+async fn put_organization_user(
     org_id: String,
     org_user_id: String,
     data: JsonUpcase<EditUserData>,
     headers: AdminHeaders,
     conn: DbConn,
 ) -> EmptyResult {
-    edit_user(org_id, org_user_id, data, headers, conn)
+    edit_user(org_id, org_user_id, data, headers, conn).await
 }
 
 #[post("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
-fn edit_user(
+async fn edit_user(
     org_id: String,
     org_user_id: String,
     data: JsonUpcase<EditUserData>,
@@ -930,7 +972,7 @@ fn edit_user(
         None => err!("Invalid type"),
     };
 
-    let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn) {
+    let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn).await {
         Some(user) => user,
         None => err!("The specified user isn't member of the organization"),
     };
@@ -948,7 +990,7 @@ fn edit_user(
 
     if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner {
         // Removing owner permmission, check that there are at least another owner
-        let num_owners = UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).len();
+        let num_owners = UserOrganization::find_by_org_and_type(&org_id, UserOrgType::Owner as i32, &conn).await.len();
 
         if num_owners <= 1 {
             err!("Can't delete the last owner")
@@ -959,14 +1001,14 @@ fn edit_user(
     user_to_edit.atype = new_type as i32;
 
     // Delete all the odd collections
-    for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn) {
-        c.delete(&conn)?;
+    for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn).await {
+        c.delete(&conn).await?;
     }
 
     // If no accessAll, add the collections received
     if !data.AccessAll {
         for col in data.Collections.iter().flatten() {
-            match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
+            match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn).await {
                 None => err!("Collection not found in Organization"),
                 Some(collection) => {
                     CollectionUser::save(
@@ -975,22 +1017,28 @@ fn edit_user(
                         col.ReadOnly,
                         col.HidePasswords,
                         &conn,
-                    )?;
+                    )
+                    .await?;
                 }
             }
         }
     }
 
-    user_to_edit.save(&conn)
+    user_to_edit.save(&conn).await
 }
 
 #[delete("/organizations/<org_id>/users", data = "<data>")]
-fn bulk_delete_user(org_id: String, data: JsonUpcase<OrgBulkIds>, headers: AdminHeaders, conn: DbConn) -> Json<Value> {
+async fn bulk_delete_user(
+    org_id: String,
+    data: JsonUpcase<OrgBulkIds>,
+    headers: AdminHeaders,
+    conn: DbConn,
+) -> Json<Value> {
     let data: OrgBulkIds = data.into_inner().data;
 
     let mut bulk_response = Vec::new();
     for org_user_id in data.Ids {
-        let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &conn) {
+        let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &conn).await {
             Ok(_) => String::from(""),
             Err(e) => format!("{:?}", e),
         };
@@ -1012,12 +1060,12 @@ fn bulk_delete_user(org_id: String, data: JsonUpcase<OrgBulkIds>, headers: Admin
 }
 
 #[delete("/organizations/<org_id>/users/<org_user_id>")]
-fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
-    _delete_user(&org_id, &org_user_id, &headers, &conn)
+async fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
+    _delete_user(&org_id, &org_user_id, &headers, &conn).await
 }
 
-fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, conn: &DbConn) -> EmptyResult {
-    let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn) {
+async fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, conn: &DbConn) -> EmptyResult {
+    let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
         Some(user) => user,
         None => err!("User to delete isn't member of the organization"),
     };
@@ -1028,23 +1076,28 @@ fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, conn: &
 
     if user_to_delete.atype == UserOrgType::Owner {
         // Removing owner, check that there are at least another owner
-        let num_owners = UserOrganization::find_by_org_and_type(org_id, UserOrgType::Owner as i32, conn).len();
+        let num_owners = UserOrganization::find_by_org_and_type(org_id, UserOrgType::Owner as i32, conn).await.len();
 
         if num_owners <= 1 {
             err!("Can't delete the last owner")
         }
     }
 
-    user_to_delete.delete(conn)
+    user_to_delete.delete(conn).await
 }
 
 #[post("/organizations/<org_id>/users/<org_user_id>/delete")]
-fn post_delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
-    delete_user(org_id, org_user_id, headers, conn)
+async fn post_delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
+    delete_user(org_id, org_user_id, headers, conn).await
 }
 
 #[post("/organizations/<org_id>/users/public-keys", data = "<data>")]
-fn bulk_public_keys(org_id: String, data: JsonUpcase<OrgBulkIds>, _headers: AdminHeaders, conn: DbConn) -> Json<Value> {
+async fn bulk_public_keys(
+    org_id: String,
+    data: JsonUpcase<OrgBulkIds>,
+    _headers: AdminHeaders,
+    conn: DbConn,
+) -> Json<Value> {
     let data: OrgBulkIds = data.into_inner().data;
 
     let mut bulk_response = Vec::new();
@@ -1052,8 +1105,8 @@ fn bulk_public_keys(org_id: String, data: JsonUpcase<OrgBulkIds>, _headers: Admi
     // If the user does not exists, just ignore it, and do not return any information regarding that UserOrg UUID.
     // The web-vault will then ignore that user for the folowing steps.
     for user_org_id in data.Ids {
-        match UserOrganization::find_by_uuid_and_org(&user_org_id, &org_id, &conn) {
-            Some(user_org) => match User::find_by_uuid(&user_org.user_uuid, &conn) {
+        match UserOrganization::find_by_uuid_and_org(&user_org_id, &org_id, &conn).await {
+            Some(user_org) => match User::find_by_uuid(&user_org.user_uuid, &conn).await {
                 Some(user) => bulk_response.push(json!(
                     {
                         "Object": "organizationUserPublicKeyResponseModel",
@@ -1096,29 +1149,27 @@ struct RelationsData {
 }
 
 #[post("/ciphers/import-organization?<query..>", data = "<data>")]
-fn post_org_import(
+async fn post_org_import(
     query: OrgIdData,
     data: JsonUpcase<ImportData>,
     headers: AdminHeaders,
     conn: DbConn,
-    nt: Notify,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let data: ImportData = data.into_inner().data;
     let org_id = query.organization_id;
 
-    // Read and create the collections
-    let collections: Vec<_> = data
-        .Collections
-        .into_iter()
-        .map(|coll| {
+    let collections = stream::iter(data.Collections)
+        .then(|coll| async {
             let collection = Collection::new(org_id.clone(), coll.Name);
-            if collection.save(&conn).is_err() {
+            if collection.save(&conn).await.is_err() {
                 err!("Failed to create Collection");
             }
 
             Ok(collection)
         })
-        .collect();
+        .collect::<Vec<_>>()
+        .await;
 
     // Read the relations between collections and ciphers
     let mut relations = Vec::new();
@@ -1128,17 +1179,16 @@ fn post_org_import(
 
     let headers: Headers = headers.into();
 
-    // Read and create the ciphers
-    let ciphers: Vec<_> = data
-        .Ciphers
-        .into_iter()
-        .map(|cipher_data| {
+    let ciphers = stream::iter(data.Ciphers)
+        .then(|cipher_data| async {
             let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
             update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::CipherCreate)
+                .await
                 .ok();
             cipher
         })
-        .collect();
+        .collect::<Vec<Cipher>>()
+        .await;
 
     // Assign the collections
     for (cipher_index, coll_index) in relations {
@@ -1149,16 +1199,16 @@ fn post_org_import(
             Err(_) => err!("Failed to assign to collection"),
         };
 
-        CollectionCipher::save(cipher_id, coll_id, &conn)?;
+        CollectionCipher::save(cipher_id, coll_id, &conn).await?;
     }
 
     let mut user = headers.user;
-    user.update_revision(&conn)
+    user.update_revision(&conn).await
 }
 
 #[get("/organizations/<org_id>/policies")]
-fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json<Value> {
-    let policies = OrgPolicy::find_by_org(&org_id, &conn);
+async fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json<Value> {
+    let policies = OrgPolicy::find_by_org(&org_id, &conn).await;
     let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
 
     Json(json!({
@@ -1169,7 +1219,7 @@ fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json<V
 }
 
 #[get("/organizations/<org_id>/policies/token?<token>")]
-fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResult {
+async fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResult {
     let invite = crate::auth::decode_invite(&token)?;
 
     let invite_org_id = match invite.org_id {
@@ -1182,7 +1232,7 @@ fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResul
     }
 
     // TODO: We receive the invite token as ?token=<>, validate it contains the org id
-    let policies = OrgPolicy::find_by_org(&org_id, &conn);
+    let policies = OrgPolicy::find_by_org(&org_id, &conn).await;
     let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
 
     Ok(Json(json!({
@@ -1193,13 +1243,13 @@ fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResul
 }
 
 #[get("/organizations/<org_id>/policies/<pol_type>")]
-fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
+async fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
     let pol_type_enum = match OrgPolicyType::from_i32(pol_type) {
         Some(pt) => pt,
         None => err!("Invalid or unsupported policy type"),
     };
 
-    let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) {
+    let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn).await {
         Some(p) => p,
         None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()),
     };
@@ -1216,7 +1266,7 @@ struct PolicyData {
 }
 
 #[put("/organizations/<org_id>/policies/<pol_type>", data = "<data>")]
-fn put_policy(
+async fn put_policy(
     org_id: String,
     pol_type: i32,
     data: Json<PolicyData>,
@@ -1232,10 +1282,8 @@ fn put_policy(
 
     // If enabling the TwoFactorAuthentication policy, remove this org's members that do have 2FA
     if pol_type_enum == OrgPolicyType::TwoFactorAuthentication && data.enabled {
-        let org_members = UserOrganization::find_by_org(&org_id, &conn);
-
-        for member in org_members.into_iter() {
-            let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &conn).is_empty();
+        for member in UserOrganization::find_by_org(&org_id, &conn).await.into_iter() {
+            let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &conn).await.is_empty();
 
             // Policy only applies to non-Owner/non-Admin members who have accepted joining the org
             if user_twofactor_disabled
@@ -1243,24 +1291,23 @@ fn put_policy(
                 && member.status != UserOrgStatus::Invited as i32
             {
                 if CONFIG.mail_enabled() {
-                    let org = Organization::find_by_uuid(&member.org_uuid, &conn).unwrap();
-                    let user = User::find_by_uuid(&member.user_uuid, &conn).unwrap();
+                    let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap();
+                    let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap();
 
                     mail::send_2fa_removed_from_org(&user.email, &org.name)?;
                 }
-                member.delete(&conn)?;
+                member.delete(&conn).await?;
             }
         }
     }
 
     // If enabling the SingleOrg policy, remove this org's members that are members of other orgs
     if pol_type_enum == OrgPolicyType::SingleOrg && data.enabled {
-        let org_members = UserOrganization::find_by_org(&org_id, &conn);
-
-        for member in org_members.into_iter() {
+        for member in UserOrganization::find_by_org(&org_id, &conn).await.into_iter() {
             // Policy only applies to non-Owner/non-Admin members who have accepted joining the org
             if member.atype < UserOrgType::Admin && member.status != UserOrgStatus::Invited as i32 {
                 let is_member_of_another_org = UserOrganization::find_any_state_by_user(&member.user_uuid, &conn)
+                    .await
                     .into_iter()
                     // Other UserOrganization's where they have accepted being a member of
                     .filter(|uo| uo.uuid != member.uuid && uo.status != UserOrgStatus::Invited as i32)
@@ -1269,25 +1316,25 @@ fn put_policy(
 
                 if is_member_of_another_org {
                     if CONFIG.mail_enabled() {
-                        let org = Organization::find_by_uuid(&member.org_uuid, &conn).unwrap();
-                        let user = User::find_by_uuid(&member.user_uuid, &conn).unwrap();
+                        let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap();
+                        let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap();
 
                         mail::send_single_org_removed_from_org(&user.email, &org.name)?;
                     }
-                    member.delete(&conn)?;
+                    member.delete(&conn).await?;
                 }
             }
         }
     }
 
-    let mut policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) {
+    let mut policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn).await {
         Some(p) => p,
         None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()),
     };
 
     policy.enabled = data.enabled;
     policy.data = serde_json::to_string(&data.data)?;
-    policy.save(&conn)?;
+    policy.save(&conn).await?;
 
     Ok(Json(policy.to_json()))
 }
@@ -1360,7 +1407,7 @@ struct OrgImportData {
 }
 
 #[post("/organizations/<org_id>/import", data = "<data>")]
-fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data = data.into_inner().data;
 
     // TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way
@@ -1369,7 +1416,7 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
     // as opposed to upstream which only removes auto-imported users.
 
     // User needs to be admin or owner to use the Directry Connector
-    match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
+    match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await {
         Some(user_org) if user_org.atype >= UserOrgType::Admin => { /* Okay, nothing to do */ }
         Some(_) => err!("User has insufficient permissions to use Directory Connector"),
         None => err!("User not part of organization"),
@@ -1378,13 +1425,13 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
     for user_data in &data.Users {
         if user_data.Deleted {
             // If user is marked for deletion and it exists, delete it
-            if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn) {
-                user_org.delete(&conn)?;
+            if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).await {
+                user_org.delete(&conn).await?;
             }
 
         // If user is not part of the organization, but it exists
-        } else if UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).is_none() {
-            if let Some(user) = User::find_by_mail(&user_data.Email, &conn) {
+        } else if UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).await.is_none() {
+            if let Some(user) = User::find_by_mail(&user_data.Email, &conn).await {
                 let user_org_status = if CONFIG.mail_enabled() {
                     UserOrgStatus::Invited as i32
                 } else {
@@ -1396,10 +1443,10 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
                 new_org_user.atype = UserOrgType::User as i32;
                 new_org_user.status = user_org_status;
 
-                new_org_user.save(&conn)?;
+                new_org_user.save(&conn).await?;
 
                 if CONFIG.mail_enabled() {
-                    let org_name = match Organization::find_by_uuid(&org_id, &conn) {
+                    let org_name = match Organization::find_by_uuid(&org_id, &conn).await {
                         Some(org) => org.name,
                         None => err!("Error looking up organization"),
                     };
@@ -1419,10 +1466,10 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
 
     // If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
     if data.OverwriteExisting {
-        for user_org in UserOrganization::find_by_org_and_type(&org_id, UserOrgType::User as i32, &conn) {
-            if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).map(|u| u.email) {
+        for user_org in UserOrganization::find_by_org_and_type(&org_id, UserOrgType::User as i32, &conn).await {
+            if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).await.map(|u| u.email) {
                 if !data.Users.iter().any(|u| u.Email == user_email) {
-                    user_org.delete(&conn)?;
+                    user_org.delete(&conn).await?;
                 }
             }
         }
diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs
index 91253422..2a33aebf 100644
--- a/src/api/core/sends.rs
+++ b/src/api/core/sends.rs
@@ -35,7 +35,7 @@ pub fn routes() -> Vec<rocket::Route> {
 pub async fn purge_sends(pool: DbPool) {
     debug!("Purging sends");
     if let Ok(conn) = pool.get().await {
-        Send::purge(&conn);
+        Send::purge(&conn).await;
     } else {
         error!("Failed to get DB connection while purging sends")
     }
@@ -68,10 +68,10 @@ struct SendData {
 ///
 /// There is also a Vaultwarden-specific `sends_allowed` config setting that
 /// controls this policy globally.
-fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyResult {
+async fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyResult {
     let user_uuid = &headers.user.uuid;
     let policy_type = OrgPolicyType::DisableSend;
-    if !CONFIG.sends_allowed() || OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) {
+    if !CONFIG.sends_allowed() || OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn).await {
         err!("Due to an Enterprise Policy, you are only able to delete an existing Send.")
     }
     Ok(())
@@ -83,10 +83,10 @@ fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyResult
 /// but is allowed to remove this option from an existing Send.
 ///
 /// Ref: https://bitwarden.com/help/article/policies/#send-options
-fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &DbConn) -> EmptyResult {
+async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &DbConn) -> EmptyResult {
     let user_uuid = &headers.user.uuid;
     let hide_email = data.HideEmail.unwrap_or(false);
-    if hide_email && OrgPolicy::is_hide_email_disabled(user_uuid, conn) {
+    if hide_email && OrgPolicy::is_hide_email_disabled(user_uuid, conn).await {
         err!(
             "Due to an Enterprise Policy, you are not allowed to hide your email address \
               from recipients when creating or editing a Send."
@@ -95,7 +95,7 @@ fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &
     Ok(())
 }
 
-fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
+async fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
     let data_val = if data.Type == SendType::Text as i32 {
         data.Text
     } else if data.Type == SendType::File as i32 {
@@ -117,7 +117,7 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
         );
     }
 
-    let mut send = Send::new(data.Type, data.Name, data_str, data.Key, data.DeletionDate.naive_utc());
+    let mut send = Send::new(data.Type, data.Name, data_str, data.Key, data.DeletionDate.naive_utc()).await;
     send.user_uuid = Some(user_uuid);
     send.notes = data.Notes;
     send.max_access_count = match data.MaxAccessCount {
@@ -135,9 +135,9 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
 }
 
 #[get("/sends")]
-fn get_sends(headers: Headers, conn: DbConn) -> Json<Value> {
+async fn get_sends(headers: Headers, conn: DbConn) -> Json<Value> {
     let sends = Send::find_by_user(&headers.user.uuid, &conn);
-    let sends_json: Vec<Value> = sends.iter().map(|s| s.to_json()).collect();
+    let sends_json: Vec<Value> = sends.await.iter().map(|s| s.to_json()).collect();
 
     Json(json!({
       "Data": sends_json,
@@ -147,8 +147,8 @@ fn get_sends(headers: Headers, conn: DbConn) -> Json<Value> {
 }
 
 #[get("/sends/<uuid>")]
-fn get_send(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
-    let send = match Send::find_by_uuid(&uuid, &conn) {
+async fn get_send(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
+    let send = match Send::find_by_uuid(&uuid, &conn).await {
         Some(send) => send,
         None => err!("Send not found"),
     };
@@ -161,19 +161,19 @@ fn get_send(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
 }
 
 #[post("/sends", data = "<data>")]
-fn post_send(data: JsonUpcase<SendData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    enforce_disable_send_policy(&headers, &conn)?;
+async fn post_send(data: JsonUpcase<SendData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
+    enforce_disable_send_policy(&headers, &conn).await?;
 
     let data: SendData = data.into_inner().data;
-    enforce_disable_hide_email_policy(&data, &headers, &conn)?;
+    enforce_disable_hide_email_policy(&data, &headers, &conn).await?;
 
     if data.Type == SendType::File as i32 {
         err!("File sends should use /api/sends/file")
     }
 
-    let mut send = create_send(data, headers.user.uuid)?;
-    send.save(&conn)?;
-    nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&conn));
+    let mut send = create_send(data, headers.user.uuid).await?;
+    send.save(&conn).await?;
+    nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&conn).await);
 
     Ok(Json(send.to_json()))
 }
@@ -186,7 +186,7 @@ struct UploadData<'f> {
 
 #[post("/sends/file", format = "multipart/form-data", data = "<data>")]
 async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
-    enforce_disable_send_policy(&headers, &conn)?;
+    enforce_disable_send_policy(&headers, &conn).await?;
 
     let UploadData {
         model,
@@ -194,7 +194,7 @@ async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, conn: DbCo
     } = data.into_inner();
     let model = model.into_inner().data;
 
-    enforce_disable_hide_email_policy(&model, &headers, &conn)?;
+    enforce_disable_hide_email_policy(&model, &headers, &conn).await?;
 
     // Get the file length and add an extra 5% to avoid issues
     const SIZE_525_MB: u64 = 550_502_400;
@@ -202,7 +202,7 @@ async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, conn: DbCo
     let size_limit = match CONFIG.user_attachment_limit() {
         Some(0) => err!("File uploads are disabled"),
         Some(limit_kb) => {
-            let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &conn);
+            let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &conn).await;
             if left <= 0 {
                 err!("Attachment storage limit reached! Delete some attachments to free up space")
             }
@@ -211,7 +211,7 @@ async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, conn: DbCo
         None => SIZE_525_MB,
     };
 
-    let mut send = create_send(model, headers.user.uuid)?;
+    let mut send = create_send(model, headers.user.uuid).await?;
     if send.atype != SendType::File as i32 {
         err!("Send content is not a file");
     }
@@ -236,8 +236,8 @@ async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, conn: DbCo
     send.data = serde_json::to_string(&data_value)?;
 
     // Save the changes in the database
-    send.save(&conn)?;
-    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn));
+    send.save(&conn).await?;
+    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await);
 
     Ok(Json(send.to_json()))
 }
@@ -249,8 +249,8 @@ pub struct SendAccessData {
 }
 
 #[post("/sends/access/<access_id>", data = "<data>")]
-fn post_access(access_id: String, data: JsonUpcase<SendAccessData>, conn: DbConn, ip: ClientIp) -> JsonResult {
-    let mut send = match Send::find_by_access_id(&access_id, &conn) {
+async fn post_access(access_id: String, data: JsonUpcase<SendAccessData>, conn: DbConn, ip: ClientIp) -> JsonResult {
+    let mut send = match Send::find_by_access_id(&access_id, &conn).await {
         Some(s) => s,
         None => err_code!(SEND_INACCESSIBLE_MSG, 404),
     };
@@ -288,20 +288,20 @@ fn post_access(access_id: String, data: JsonUpcase<SendAccessData>, conn: DbConn
         send.access_count += 1;
     }
 
-    send.save(&conn)?;
+    send.save(&conn).await?;
 
-    Ok(Json(send.to_json_access(&conn)))
+    Ok(Json(send.to_json_access(&conn).await))
 }
 
 #[post("/sends/<send_id>/access/file/<file_id>", data = "<data>")]
-fn post_access_file(
+async fn post_access_file(
     send_id: String,
     file_id: String,
     data: JsonUpcase<SendAccessData>,
     host: Host,
     conn: DbConn,
 ) -> JsonResult {
-    let mut send = match Send::find_by_uuid(&send_id, &conn) {
+    let mut send = match Send::find_by_uuid(&send_id, &conn).await {
         Some(s) => s,
         None => err_code!(SEND_INACCESSIBLE_MSG, 404),
     };
@@ -336,7 +336,7 @@ fn post_access_file(
 
     send.access_count += 1;
 
-    send.save(&conn)?;
+    send.save(&conn).await?;
 
     let token_claims = crate::auth::generate_send_claims(&send_id, &file_id);
     let token = crate::auth::encode_jwt(&token_claims);
@@ -358,13 +358,19 @@ async fn download_send(send_id: SafeString, file_id: SafeString, t: String) -> O
 }
 
 #[put("/sends/<id>", data = "<data>")]
-fn put_send(id: String, data: JsonUpcase<SendData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    enforce_disable_send_policy(&headers, &conn)?;
+async fn put_send(
+    id: String,
+    data: JsonUpcase<SendData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    enforce_disable_send_policy(&headers, &conn).await?;
 
     let data: SendData = data.into_inner().data;
-    enforce_disable_hide_email_policy(&data, &headers, &conn)?;
+    enforce_disable_hide_email_policy(&data, &headers, &conn).await?;
 
-    let mut send = match Send::find_by_uuid(&id, &conn) {
+    let mut send = match Send::find_by_uuid(&id, &conn).await {
         Some(s) => s,
         None => err!("Send not found"),
     };
@@ -411,15 +417,15 @@ fn put_send(id: String, data: JsonUpcase<SendData>, headers: Headers, conn: DbCo
         send.set_password(Some(&password));
     }
 
-    send.save(&conn)?;
-    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn));
+    send.save(&conn).await?;
+    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await);
 
     Ok(Json(send.to_json()))
 }
 
 #[delete("/sends/<id>")]
-fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
-    let send = match Send::find_by_uuid(&id, &conn) {
+async fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
+    let send = match Send::find_by_uuid(&id, &conn).await {
         Some(s) => s,
         None => err!("Send not found"),
     };
@@ -428,17 +434,17 @@ fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyR
         err!("Send is not owned by user")
     }
 
-    send.delete(&conn)?;
-    nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&conn));
+    send.delete(&conn).await?;
+    nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&conn).await);
 
     Ok(())
 }
 
 #[put("/sends/<id>/remove-password")]
-fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
-    enforce_disable_send_policy(&headers, &conn)?;
+async fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult {
+    enforce_disable_send_policy(&headers, &conn).await?;
 
-    let mut send = match Send::find_by_uuid(&id, &conn) {
+    let mut send = match Send::find_by_uuid(&id, &conn).await {
         Some(s) => s,
         None => err!("Send not found"),
     };
@@ -448,8 +454,8 @@ fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Notify) -
     }
 
     send.set_password(None);
-    send.save(&conn)?;
-    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn));
+    send.save(&conn).await?;
+    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await);
 
     Ok(Json(send.to_json()))
 }
diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs
index 7d80cb54..40ff233c 100644
--- a/src/api/core/two_factor/authenticator.rs
+++ b/src/api/core/two_factor/authenticator.rs
@@ -21,7 +21,7 @@ pub fn routes() -> Vec<Route> {
 }
 
 #[post("/two-factor/get-authenticator", data = "<data>")]
-fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: PasswordData = data.into_inner().data;
     let user = headers.user;
 
@@ -30,7 +30,7 @@ fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn
     }
 
     let type_ = TwoFactorType::Authenticator as i32;
-    let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn);
+    let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await;
 
     let (enabled, key) = match twofactor {
         Some(tf) => (true, tf.data),
@@ -53,7 +53,7 @@ struct EnableAuthenticatorData {
 }
 
 #[post("/two-factor/authenticator", data = "<data>")]
-fn activate_authenticator(
+async fn activate_authenticator(
     data: JsonUpcase<EnableAuthenticatorData>,
     headers: Headers,
     ip: ClientIp,
@@ -81,9 +81,9 @@ fn activate_authenticator(
     }
 
     // Validate the token provided with the key, and save new twofactor
-    validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &conn)?;
+    validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &conn).await?;
 
-    _generate_recover_code(&mut user, &conn);
+    _generate_recover_code(&mut user, &conn).await;
 
     Ok(Json(json!({
         "Enabled": true,
@@ -93,16 +93,16 @@ fn activate_authenticator(
 }
 
 #[put("/two-factor/authenticator", data = "<data>")]
-fn activate_authenticator_put(
+async fn activate_authenticator_put(
     data: JsonUpcase<EnableAuthenticatorData>,
     headers: Headers,
     ip: ClientIp,
     conn: DbConn,
 ) -> JsonResult {
-    activate_authenticator(data, headers, ip, conn)
+    activate_authenticator(data, headers, ip, conn).await
 }
 
-pub fn validate_totp_code_str(
+pub async fn validate_totp_code_str(
     user_uuid: &str,
     totp_code: &str,
     secret: &str,
@@ -113,10 +113,16 @@ pub fn validate_totp_code_str(
         err!("TOTP code is not a number");
     }
 
-    validate_totp_code(user_uuid, totp_code, secret, ip, conn)
+    validate_totp_code(user_uuid, totp_code, secret, ip, conn).await
 }
 
-pub fn validate_totp_code(user_uuid: &str, totp_code: &str, secret: &str, ip: &ClientIp, conn: &DbConn) -> EmptyResult {
+pub async fn validate_totp_code(
+    user_uuid: &str,
+    totp_code: &str,
+    secret: &str,
+    ip: &ClientIp,
+    conn: &DbConn,
+) -> EmptyResult {
     use totp_lite::{totp_custom, Sha1};
 
     let decoded_secret = match BASE32.decode(secret.as_bytes()) {
@@ -124,10 +130,11 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: &str, secret: &str, ip: &C
         Err(_) => err!("Invalid TOTP secret"),
     };
 
-    let mut twofactor = match TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Authenticator as i32, conn) {
-        Some(tf) => tf,
-        _ => TwoFactor::new(user_uuid.to_string(), TwoFactorType::Authenticator, secret.to_string()),
-    };
+    let mut twofactor =
+        match TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Authenticator as i32, conn).await {
+            Some(tf) => tf,
+            _ => TwoFactor::new(user_uuid.to_string(), TwoFactorType::Authenticator, secret.to_string()),
+        };
 
     // The amount of steps back and forward in time
     // Also check if we need to disable time drifted TOTP codes.
@@ -156,7 +163,7 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: &str, secret: &str, ip: &C
             // Save the last used time step so only totp time steps higher then this one are allowed.
             // This will also save a newly created twofactor if the code is correct.
             twofactor.last_used = time_step as i32;
-            twofactor.save(conn)?;
+            twofactor.save(conn).await?;
             return Ok(());
         } else if generated == totp_code && time_step <= twofactor.last_used as i64 {
             warn!("This TOTP or a TOTP code within {} steps back or forward has already been used!", steps);
diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs
index 1450cef0..ccfa05be 100644
--- a/src/api/core/two_factor/duo.rs
+++ b/src/api/core/two_factor/duo.rs
@@ -89,14 +89,14 @@ impl DuoStatus {
 const DISABLED_MESSAGE_DEFAULT: &str = "<To use the global Duo keys, please leave these fields untouched>";
 
 #[post("/two-factor/get-duo", data = "<data>")]
-fn get_duo(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_duo(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: PasswordData = data.into_inner().data;
 
     if !headers.user.check_valid_password(&data.MasterPasswordHash) {
         err!("Invalid password");
     }
 
-    let data = get_user_duo_data(&headers.user.uuid, &conn);
+    let data = get_user_duo_data(&headers.user.uuid, &conn).await;
 
     let (enabled, data) = match data {
         DuoStatus::Global(_) => (true, Some(DuoData::secret())),
@@ -171,9 +171,9 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: D
 
     let type_ = TwoFactorType::Duo;
     let twofactor = TwoFactor::new(user.uuid.clone(), type_, data_str);
-    twofactor.save(&conn)?;
+    twofactor.save(&conn).await?;
 
-    _generate_recover_code(&mut user, &conn);
+    _generate_recover_code(&mut user, &conn).await;
 
     Ok(Json(json!({
         "Enabled": true,
@@ -223,11 +223,11 @@ const AUTH_PREFIX: &str = "AUTH";
 const DUO_PREFIX: &str = "TX";
 const APP_PREFIX: &str = "APP";
 
-fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus {
+async fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus {
     let type_ = TwoFactorType::Duo as i32;
 
     // If the user doesn't have an entry, disabled
-    let twofactor = match TwoFactor::find_by_user_and_type(uuid, type_, conn) {
+    let twofactor = match TwoFactor::find_by_user_and_type(uuid, type_, conn).await {
         Some(t) => t,
         None => return DuoStatus::Disabled(DuoData::global().is_some()),
     };
@@ -247,19 +247,20 @@ fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus {
 }
 
 // let (ik, sk, ak, host) = get_duo_keys();
-fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, String, String, String)> {
-    let data = User::find_by_mail(email, conn)
-        .and_then(|u| get_user_duo_data(&u.uuid, conn).data())
-        .or_else(DuoData::global)
-        .map_res("Can't fetch Duo keys")?;
+async fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, String, String, String)> {
+    let data = match User::find_by_mail(email, conn).await {
+        Some(u) => get_user_duo_data(&u.uuid, conn).await.data(),
+        _ => DuoData::global(),
+    }
+    .map_res("Can't fetch Duo Keys")?;
 
     Ok((data.ik, data.sk, CONFIG.get_duo_akey(), data.host))
 }
 
-pub fn generate_duo_signature(email: &str, conn: &DbConn) -> ApiResult<(String, String)> {
+pub async fn generate_duo_signature(email: &str, conn: &DbConn) -> ApiResult<(String, String)> {
     let now = Utc::now().timestamp();
 
-    let (ik, sk, ak, host) = get_duo_keys_email(email, conn)?;
+    let (ik, sk, ak, host) = get_duo_keys_email(email, conn).await?;
 
     let duo_sign = sign_duo_values(&sk, email, &ik, DUO_PREFIX, now + DUO_EXPIRE);
     let app_sign = sign_duo_values(&ak, email, &ik, APP_PREFIX, now + APP_EXPIRE);
@@ -274,7 +275,7 @@ fn sign_duo_values(key: &str, email: &str, ikey: &str, prefix: &str, expire: i64
     format!("{}|{}", cookie, crypto::hmac_sign(key, &cookie))
 }
 
-pub fn validate_duo_login(email: &str, response: &str, conn: &DbConn) -> EmptyResult {
+pub async fn validate_duo_login(email: &str, response: &str, conn: &DbConn) -> EmptyResult {
     // email is as entered by the user, so it needs to be normalized before
     // comparison with auth_user below.
     let email = &email.to_lowercase();
@@ -289,7 +290,7 @@ pub fn validate_duo_login(email: &str, response: &str, conn: &DbConn) -> EmptyRe
 
     let now = Utc::now().timestamp();
 
-    let (ik, sk, ak, _host) = get_duo_keys_email(email, conn)?;
+    let (ik, sk, ak, _host) = get_duo_keys_email(email, conn).await?;
 
     let auth_user = parse_duo_values(&sk, auth_sig, &ik, AUTH_PREFIX, now)?;
     let app_user = parse_duo_values(&ak, app_sig, &ik, APP_PREFIX, now)?;
diff --git a/src/api/core/two_factor/email.rs b/src/api/core/two_factor/email.rs
index 51487ee3..f6dfd77a 100644
--- a/src/api/core/two_factor/email.rs
+++ b/src/api/core/two_factor/email.rs
@@ -28,13 +28,13 @@ struct SendEmailLoginData {
 /// User is trying to login and wants to use email 2FA.
 /// Does not require Bearer token
 #[post("/two-factor/send-email-login", data = "<data>")] // JsonResult
-fn send_email_login(data: JsonUpcase<SendEmailLoginData>, conn: DbConn) -> EmptyResult {
+async fn send_email_login(data: JsonUpcase<SendEmailLoginData>, conn: DbConn) -> EmptyResult {
     let data: SendEmailLoginData = data.into_inner().data;
 
     use crate::db::models::User;
 
     // Get the user
-    let user = match User::find_by_mail(&data.Email, &conn) {
+    let user = match User::find_by_mail(&data.Email, &conn).await {
         Some(user) => user,
         None => err!("Username or password is incorrect. Try again."),
     };
@@ -48,22 +48,23 @@ fn send_email_login(data: JsonUpcase<SendEmailLoginData>, conn: DbConn) -> Empty
         err!("Email 2FA is disabled")
     }
 
-    send_token(&user.uuid, &conn)?;
+    send_token(&user.uuid, &conn).await?;
 
     Ok(())
 }
 
 /// Generate the token, save the data for later verification and send email to user
-pub fn send_token(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+pub async fn send_token(user_uuid: &str, conn: &DbConn) -> EmptyResult {
     let type_ = TwoFactorType::Email as i32;
-    let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, type_, conn).map_res("Two factor not found")?;
+    let mut twofactor =
+        TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await.map_res("Two factor not found")?;
 
     let generated_token = crypto::generate_email_token(CONFIG.email_token_size());
 
     let mut twofactor_data = EmailTokenData::from_json(&twofactor.data)?;
     twofactor_data.set_token(generated_token);
     twofactor.data = twofactor_data.to_json();
-    twofactor.save(conn)?;
+    twofactor.save(conn).await?;
 
     mail::send_token(&twofactor_data.email, &twofactor_data.last_token.map_res("Token is empty")?)?;
 
@@ -72,7 +73,7 @@ pub fn send_token(user_uuid: &str, conn: &DbConn) -> EmptyResult {
 
 /// When user clicks on Manage email 2FA show the user the related information
 #[post("/two-factor/get-email", data = "<data>")]
-fn get_email(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_email(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: PasswordData = data.into_inner().data;
     let user = headers.user;
 
@@ -80,13 +81,14 @@ fn get_email(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) ->
         err!("Invalid password");
     }
 
-    let (enabled, mfa_email) = match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn) {
-        Some(x) => {
-            let twofactor_data = EmailTokenData::from_json(&x.data)?;
-            (true, json!(twofactor_data.email))
-        }
-        _ => (false, json!(null)),
-    };
+    let (enabled, mfa_email) =
+        match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn).await {
+            Some(x) => {
+                let twofactor_data = EmailTokenData::from_json(&x.data)?;
+                (true, json!(twofactor_data.email))
+            }
+            _ => (false, json!(null)),
+        };
 
     Ok(Json(json!({
         "Email": mfa_email,
@@ -105,7 +107,7 @@ struct SendEmailData {
 
 /// Send a verification email to the specified email address to check whether it exists/belongs to user.
 #[post("/two-factor/send-email", data = "<data>")]
-fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, conn: DbConn) -> EmptyResult {
+async fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, conn: DbConn) -> EmptyResult {
     let data: SendEmailData = data.into_inner().data;
     let user = headers.user;
 
@@ -119,8 +121,8 @@ fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, conn: DbConn) -
 
     let type_ = TwoFactorType::Email as i32;
 
-    if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn) {
-        tf.delete(&conn)?;
+    if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await {
+        tf.delete(&conn).await?;
     }
 
     let generated_token = crypto::generate_email_token(CONFIG.email_token_size());
@@ -128,7 +130,7 @@ fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, conn: DbConn) -
 
     // Uses EmailVerificationChallenge as type to show that it's not verified yet.
     let twofactor = TwoFactor::new(user.uuid, TwoFactorType::EmailVerificationChallenge, twofactor_data.to_json());
-    twofactor.save(&conn)?;
+    twofactor.save(&conn).await?;
 
     mail::send_token(&twofactor_data.email, &twofactor_data.last_token.map_res("Token is empty")?)?;
 
@@ -145,7 +147,7 @@ struct EmailData {
 
 /// Verify email belongs to user and can be used for 2FA email codes.
 #[put("/two-factor/email", data = "<data>")]
-fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: EmailData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -154,7 +156,8 @@ fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
     }
 
     let type_ = TwoFactorType::EmailVerificationChallenge as i32;
-    let mut twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).map_res("Two factor not found")?;
+    let mut twofactor =
+        TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await.map_res("Two factor not found")?;
 
     let mut email_data = EmailTokenData::from_json(&twofactor.data)?;
 
@@ -170,9 +173,9 @@ fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
     email_data.reset_token();
     twofactor.atype = TwoFactorType::Email as i32;
     twofactor.data = email_data.to_json();
-    twofactor.save(&conn)?;
+    twofactor.save(&conn).await?;
 
-    _generate_recover_code(&mut user, &conn);
+    _generate_recover_code(&mut user, &conn).await;
 
     Ok(Json(json!({
         "Email": email_data.email,
@@ -182,9 +185,10 @@ fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
 }
 
 /// Validate the email code when used as TwoFactor token mechanism
-pub fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &DbConn) -> EmptyResult {
+pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &DbConn) -> EmptyResult {
     let mut email_data = EmailTokenData::from_json(data)?;
     let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn)
+        .await
         .map_res("Two factor not found")?;
     let issued_token = match &email_data.last_token {
         Some(t) => t,
@@ -197,14 +201,14 @@ pub fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &
             email_data.reset_token();
         }
         twofactor.data = email_data.to_json();
-        twofactor.save(conn)?;
+        twofactor.save(conn).await?;
 
         err!("Token is invalid")
     }
 
     email_data.reset_token();
     twofactor.data = email_data.to_json();
-    twofactor.save(conn)?;
+    twofactor.save(conn).await?;
 
     let date = NaiveDateTime::from_timestamp(email_data.token_sent, 0);
     let max_time = CONFIG.email_expiration_time() as i64;
diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs
index 105fe9eb..0f3bd14e 100644
--- a/src/api/core/two_factor/mod.rs
+++ b/src/api/core/two_factor/mod.rs
@@ -33,8 +33,8 @@ pub fn routes() -> Vec<Route> {
 }
 
 #[get("/two-factor")]
-fn get_twofactor(headers: Headers, conn: DbConn) -> Json<Value> {
-    let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);
+async fn get_twofactor(headers: Headers, conn: DbConn) -> Json<Value> {
+    let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn).await;
     let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_provider).collect();
 
     Json(json!({
@@ -68,13 +68,13 @@ struct RecoverTwoFactor {
 }
 
 #[post("/two-factor/recover", data = "<data>")]
-fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
+async fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
     let data: RecoverTwoFactor = data.into_inner().data;
 
     use crate::db::models::User;
 
     // Get the user
-    let mut user = match User::find_by_mail(&data.Email, &conn) {
+    let mut user = match User::find_by_mail(&data.Email, &conn).await {
         Some(user) => user,
         None => err!("Username or password is incorrect. Try again."),
     };
@@ -90,19 +90,19 @@ fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
     }
 
     // Remove all twofactors from the user
-    TwoFactor::delete_all_by_user(&user.uuid, &conn)?;
+    TwoFactor::delete_all_by_user(&user.uuid, &conn).await?;
 
     // Remove the recovery code, not needed without twofactors
     user.totp_recover = None;
-    user.save(&conn)?;
+    user.save(&conn).await?;
     Ok(Json(json!({})))
 }
 
-fn _generate_recover_code(user: &mut User, conn: &DbConn) {
+async fn _generate_recover_code(user: &mut User, conn: &DbConn) {
     if user.totp_recover.is_none() {
         let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
         user.totp_recover = Some(totp_recover);
-        user.save(conn).ok();
+        user.save(conn).await.ok();
     }
 }
 
@@ -114,7 +114,7 @@ struct DisableTwoFactorData {
 }
 
 #[post("/two-factor/disable", data = "<data>")]
-fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: DisableTwoFactorData = data.into_inner().data;
     let password_hash = data.MasterPasswordHash;
     let user = headers.user;
@@ -125,23 +125,24 @@ fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, c
 
     let type_ = data.Type.into_i32()?;
 
-    if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn) {
-        twofactor.delete(&conn)?;
+    if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await {
+        twofactor.delete(&conn).await?;
     }
 
-    let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &conn).is_empty();
+    let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &conn).await.is_empty();
 
     if twofactor_disabled {
-        let policy_type = OrgPolicyType::TwoFactorAuthentication;
-        let org_list = UserOrganization::find_by_user_and_policy(&user.uuid, policy_type, &conn);
-
-        for user_org in org_list.into_iter() {
+        for user_org in
+            UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &conn)
+                .await
+                .into_iter()
+        {
             if user_org.atype < UserOrgType::Admin {
                 if CONFIG.mail_enabled() {
-                    let org = Organization::find_by_uuid(&user_org.org_uuid, &conn).unwrap();
+                    let org = Organization::find_by_uuid(&user_org.org_uuid, &conn).await.unwrap();
                     mail::send_2fa_removed_from_org(&user.email, &org.name)?;
                 }
-                user_org.delete(&conn)?;
+                user_org.delete(&conn).await?;
             }
         }
     }
@@ -154,8 +155,8 @@ fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, c
 }
 
 #[put("/two-factor/disable", data = "<data>")]
-fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
-    disable_twofactor(data, headers, conn)
+async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
+    disable_twofactor(data, headers, conn).await
 }
 
 pub async fn send_incomplete_2fa_notifications(pool: DbPool) {
@@ -175,15 +176,16 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) {
 
     let now = Utc::now().naive_utc();
     let time_limit = Duration::minutes(CONFIG.incomplete_2fa_time_limit());
-    let incomplete_logins = TwoFactorIncomplete::find_logins_before(&(now - time_limit), &conn);
+    let time_before = now - time_limit;
+    let incomplete_logins = TwoFactorIncomplete::find_logins_before(&time_before, &conn).await;
     for login in incomplete_logins {
-        let user = User::find_by_uuid(&login.user_uuid, &conn).expect("User not found");
+        let user = User::find_by_uuid(&login.user_uuid, &conn).await.expect("User not found");
         info!(
             "User {} did not complete a 2FA login within the configured time limit. IP: {}",
             user.email, login.ip_address
         );
         mail::send_incomplete_2fa_login(&user.email, &login.ip_address, &login.login_time, &login.device_name)
             .expect("Error sending incomplete 2FA email");
-        login.delete(&conn).expect("Error deleting incomplete 2FA record");
+        login.delete(&conn).await.expect("Error deleting incomplete 2FA record");
     }
 }
diff --git a/src/api/core/two_factor/u2f.rs b/src/api/core/two_factor/u2f.rs
index cb24bcb3..5ae976c8 100644
--- a/src/api/core/two_factor/u2f.rs
+++ b/src/api/core/two_factor/u2f.rs
@@ -32,7 +32,7 @@ pub fn routes() -> Vec<Route> {
 }
 
 #[post("/two-factor/get-u2f", data = "<data>")]
-fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     if !CONFIG.domain_set() {
         err!("`DOMAIN` environment variable is not set. U2F disabled")
     }
@@ -42,7 +42,7 @@ fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn)
         err!("Invalid password");
     }
 
-    let (enabled, keys) = get_u2f_registrations(&headers.user.uuid, &conn)?;
+    let (enabled, keys) = get_u2f_registrations(&headers.user.uuid, &conn).await?;
     let keys_json: Vec<Value> = keys.iter().map(U2FRegistration::to_json).collect();
 
     Ok(Json(json!({
@@ -53,7 +53,7 @@ fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn)
 }
 
 #[post("/two-factor/get-u2f-challenge", data = "<data>")]
-fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: PasswordData = data.into_inner().data;
 
     if !headers.user.check_valid_password(&data.MasterPasswordHash) {
@@ -61,7 +61,7 @@ fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn
     }
 
     let _type = TwoFactorType::U2fRegisterChallenge;
-    let challenge = _create_u2f_challenge(&headers.user.uuid, _type, &conn).challenge;
+    let challenge = _create_u2f_challenge(&headers.user.uuid, _type, &conn).await.challenge;
 
     Ok(Json(json!({
         "UserId": headers.user.uuid,
@@ -137,7 +137,7 @@ impl From<RegisterResponseCopy> for RegisterResponse {
 }
 
 #[post("/two-factor/u2f", data = "<data>")]
-fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: EnableU2FData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -146,13 +146,13 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
     }
 
     let tf_type = TwoFactorType::U2fRegisterChallenge as i32;
-    let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn) {
+    let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn).await {
         Some(c) => c,
         None => err!("Can't recover challenge"),
     };
 
     let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
-    tf_challenge.delete(&conn)?;
+    tf_challenge.delete(&conn).await?;
 
     let response: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
 
@@ -172,13 +172,13 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
         migrated: None,
     };
 
-    let mut regs = get_u2f_registrations(&user.uuid, &conn)?.1;
+    let mut regs = get_u2f_registrations(&user.uuid, &conn).await?.1;
 
     // TODO: Check that there is no repeat Id
     regs.push(full_registration);
-    save_u2f_registrations(&user.uuid, &regs, &conn)?;
+    save_u2f_registrations(&user.uuid, &regs, &conn).await?;
 
-    _generate_recover_code(&mut user, &conn);
+    _generate_recover_code(&mut user, &conn).await;
 
     let keys_json: Vec<Value> = regs.iter().map(U2FRegistration::to_json).collect();
     Ok(Json(json!({
@@ -189,8 +189,8 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
 }
 
 #[put("/two-factor/u2f", data = "<data>")]
-fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
-    activate_u2f(data, headers, conn)
+async fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
+    activate_u2f(data, headers, conn).await
 }
 
 #[derive(Deserialize, Debug)]
@@ -201,7 +201,7 @@ struct DeleteU2FData {
 }
 
 #[delete("/two-factor/u2f", data = "<data>")]
-fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: DeleteU2FData = data.into_inner().data;
 
     let id = data.Id.into_i32()?;
@@ -211,7 +211,7 @@ fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -
     }
 
     let type_ = TwoFactorType::U2f as i32;
-    let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn) {
+    let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn).await {
         Some(tf) => tf,
         None => err!("U2F data not found!"),
     };
@@ -226,7 +226,7 @@ fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -
     let new_data_str = serde_json::to_string(&data)?;
 
     tf.data = new_data_str;
-    tf.save(&conn)?;
+    tf.save(&conn).await?;
 
     let keys_json: Vec<Value> = data.iter().map(U2FRegistration::to_json).collect();
 
@@ -237,23 +237,24 @@ fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -
     })))
 }
 
-fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge {
+async fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge {
     let challenge = U2F.generate_challenge().unwrap();
 
     TwoFactor::new(user_uuid.into(), type_, serde_json::to_string(&challenge).unwrap())
         .save(conn)
+        .await
         .expect("Error saving challenge");
 
     challenge
 }
 
-fn save_u2f_registrations(user_uuid: &str, regs: &[U2FRegistration], conn: &DbConn) -> EmptyResult {
-    TwoFactor::new(user_uuid.into(), TwoFactorType::U2f, serde_json::to_string(regs)?).save(conn)
+async fn save_u2f_registrations(user_uuid: &str, regs: &[U2FRegistration], conn: &DbConn) -> EmptyResult {
+    TwoFactor::new(user_uuid.into(), TwoFactorType::U2f, serde_json::to_string(regs)?).save(conn).await
 }
 
-fn get_u2f_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<U2FRegistration>), Error> {
+async fn get_u2f_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<U2FRegistration>), Error> {
     let type_ = TwoFactorType::U2f as i32;
-    let (enabled, regs) = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) {
+    let (enabled, regs) = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await {
         Some(tf) => (tf.enabled, tf.data),
         None => return Ok((false, Vec::new())), // If no data, return empty list
     };
@@ -279,7 +280,7 @@ fn get_u2f_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<U2
             }];
 
             // Save new format
-            save_u2f_registrations(user_uuid, &new_regs, conn)?;
+            save_u2f_registrations(user_uuid, &new_regs, conn).await?;
 
             new_regs
         }
@@ -297,10 +298,10 @@ fn _old_parse_registrations(registations: &str) -> Vec<Registration> {
     regs.into_iter().map(|r| serde_json::from_value(r).unwrap()).map(|Helper(r)| r).collect()
 }
 
-pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> {
-    let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn);
+pub async fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> {
+    let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn).await;
 
-    let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn)?.1.into_iter().map(|r| r.reg).collect();
+    let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.reg).collect();
 
     if registrations.is_empty() {
         err!("No U2F devices registered")
@@ -309,20 +310,20 @@ pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRe
     Ok(U2F.sign_request(challenge, registrations))
 }
 
-pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
+pub async fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
     let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
-    let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, conn);
+    let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, conn).await;
 
     let challenge = match tf_challenge {
         Some(tf_challenge) => {
             let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
-            tf_challenge.delete(conn)?;
+            tf_challenge.delete(conn).await?;
             challenge
         }
         None => err!("Can't recover login challenge"),
     };
     let response: SignResponse = serde_json::from_str(response)?;
-    let mut registrations = get_u2f_registrations(user_uuid, conn)?.1;
+    let mut registrations = get_u2f_registrations(user_uuid, conn).await?.1;
     if registrations.is_empty() {
         err!("No U2F devices registered")
     }
@@ -332,13 +333,13 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Emp
         match response {
             Ok(new_counter) => {
                 reg.counter = new_counter;
-                save_u2f_registrations(user_uuid, &registrations, conn)?;
+                save_u2f_registrations(user_uuid, &registrations, conn).await?;
 
                 return Ok(());
             }
             Err(u2f::u2ferror::U2fError::CounterTooLow) => {
                 reg.compromised = true;
-                save_u2f_registrations(user_uuid, &registrations, conn)?;
+                save_u2f_registrations(user_uuid, &registrations, conn).await?;
 
                 err!("This device might be compromised!");
             }
diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs
index 4dd0c294..bb18028d 100644
--- a/src/api/core/two_factor/webauthn.rs
+++ b/src/api/core/two_factor/webauthn.rs
@@ -80,7 +80,7 @@ impl WebauthnRegistration {
 }
 
 #[post("/two-factor/get-webauthn", data = "<data>")]
-fn get_webauthn(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn get_webauthn(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     if !CONFIG.domain_set() {
         err!("`DOMAIN` environment variable is not set. Webauthn disabled")
     }
@@ -89,7 +89,7 @@ fn get_webauthn(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn)
         err!("Invalid password");
     }
 
-    let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &conn)?;
+    let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &conn).await?;
     let registrations_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect();
 
     Ok(Json(json!({
@@ -100,12 +100,13 @@ fn get_webauthn(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn)
 }
 
 #[post("/two-factor/get-webauthn-challenge", data = "<data>")]
-fn generate_webauthn_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn generate_webauthn_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     if !headers.user.check_valid_password(&data.data.MasterPasswordHash) {
         err!("Invalid password");
     }
 
-    let registrations = get_webauthn_registrations(&headers.user.uuid, &conn)?
+    let registrations = get_webauthn_registrations(&headers.user.uuid, &conn)
+        .await?
         .1
         .into_iter()
         .map(|r| r.credential.cred_id) // We return the credentialIds to the clients to avoid double registering
@@ -121,7 +122,7 @@ fn generate_webauthn_challenge(data: JsonUpcase<PasswordData>, headers: Headers,
     )?;
 
     let type_ = TwoFactorType::WebauthnRegisterChallenge;
-    TwoFactor::new(headers.user.uuid, type_, serde_json::to_string(&state)?).save(&conn)?;
+    TwoFactor::new(headers.user.uuid, type_, serde_json::to_string(&state)?).save(&conn).await?;
 
     let mut challenge_value = serde_json::to_value(challenge.public_key)?;
     challenge_value["status"] = "ok".into();
@@ -218,7 +219,7 @@ impl From<PublicKeyCredentialCopy> for PublicKeyCredential {
 }
 
 #[post("/two-factor/webauthn", data = "<data>")]
-fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: EnableWebauthnData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -228,10 +229,10 @@ fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, con
 
     // Retrieve and delete the saved challenge state
     let type_ = TwoFactorType::WebauthnRegisterChallenge as i32;
-    let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn) {
+    let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await {
         Some(tf) => {
             let state: RegistrationState = serde_json::from_str(&tf.data)?;
-            tf.delete(&conn)?;
+            tf.delete(&conn).await?;
             state
         }
         None => err!("Can't recover challenge"),
@@ -241,7 +242,7 @@ fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, con
     let (credential, _data) =
         WebauthnConfig::load().register_credential(&data.DeviceResponse.into(), &state, |_| Ok(false))?;
 
-    let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &conn)?.1;
+    let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &conn).await?.1;
     // TODO: Check for repeated ID's
     registrations.push(WebauthnRegistration {
         id: data.Id.into_i32()?,
@@ -252,8 +253,10 @@ fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, con
     });
 
     // Save the registrations and return them
-    TwoFactor::new(user.uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?).save(&conn)?;
-    _generate_recover_code(&mut user, &conn);
+    TwoFactor::new(user.uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?)
+        .save(&conn)
+        .await?;
+    _generate_recover_code(&mut user, &conn).await;
 
     let keys_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect();
     Ok(Json(json!({
@@ -264,8 +267,8 @@ fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, con
 }
 
 #[put("/two-factor/webauthn", data = "<data>")]
-fn activate_webauthn_put(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
-    activate_webauthn(data, headers, conn)
+async fn activate_webauthn_put(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
+    activate_webauthn(data, headers, conn).await
 }
 
 #[derive(Deserialize, Debug)]
@@ -276,13 +279,14 @@ struct DeleteU2FData {
 }
 
 #[delete("/two-factor/webauthn", data = "<data>")]
-fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
     let id = data.data.Id.into_i32()?;
     if !headers.user.check_valid_password(&data.data.MasterPasswordHash) {
         err!("Invalid password");
     }
 
-    let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &conn) {
+    let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &conn).await
+    {
         Some(tf) => tf,
         None => err!("Webauthn data not found!"),
     };
@@ -296,11 +300,12 @@ fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbCo
 
     let removed_item = data.remove(item_pos);
     tf.data = serde_json::to_string(&data)?;
-    tf.save(&conn)?;
+    tf.save(&conn).await?;
     drop(tf);
 
     // If entry is migrated from u2f, delete the u2f entry as well
-    if let Some(mut u2f) = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &conn) {
+    if let Some(mut u2f) = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &conn).await
+    {
         use crate::api::core::two_factor::u2f::U2FRegistration;
         let mut data: Vec<U2FRegistration> = match serde_json::from_str(&u2f.data) {
             Ok(d) => d,
@@ -311,7 +316,7 @@ fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbCo
         let new_data_str = serde_json::to_string(&data)?;
 
         u2f.data = new_data_str;
-        u2f.save(&conn)?;
+        u2f.save(&conn).await?;
     }
 
     let keys_json: Vec<Value> = data.iter().map(WebauthnRegistration::to_json).collect();
@@ -323,18 +328,21 @@ fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbCo
     })))
 }
 
-pub fn get_webauthn_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
+pub async fn get_webauthn_registrations(
+    user_uuid: &str,
+    conn: &DbConn,
+) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
     let type_ = TwoFactorType::Webauthn as i32;
-    match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) {
+    match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await {
         Some(tf) => Ok((tf.enabled, serde_json::from_str(&tf.data)?)),
         None => Ok((false, Vec::new())), // If no data, return empty list
     }
 }
 
-pub fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResult {
+pub async fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResult {
     // Load saved credentials
     let creds: Vec<Credential> =
-        get_webauthn_registrations(user_uuid, conn)?.1.into_iter().map(|r| r.credential).collect();
+        get_webauthn_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.credential).collect();
 
     if creds.is_empty() {
         err!("No Webauthn devices registered")
@@ -346,18 +354,19 @@ pub fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResult {
 
     // Save the challenge state for later validation
     TwoFactor::new(user_uuid.into(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?)
-        .save(conn)?;
+        .save(conn)
+        .await?;
 
     // Return challenge to the clients
     Ok(Json(serde_json::to_value(response.public_key)?))
 }
 
-pub fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
+pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
     let type_ = TwoFactorType::WebauthnLoginChallenge as i32;
-    let state = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) {
+    let state = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await {
         Some(tf) => {
             let state: AuthenticationState = serde_json::from_str(&tf.data)?;
-            tf.delete(conn)?;
+            tf.delete(conn).await?;
             state
         }
         None => err!("Can't recover login challenge"),
@@ -366,7 +375,7 @@ pub fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -
     let rsp: crate::util::UpCase<PublicKeyCredentialCopy> = serde_json::from_str(response)?;
     let rsp: PublicKeyCredential = rsp.data.into();
 
-    let mut registrations = get_webauthn_registrations(user_uuid, conn)?.1;
+    let mut registrations = get_webauthn_registrations(user_uuid, conn).await?.1;
 
     // If the credential we received is migrated from U2F, enable the U2F compatibility
     //let use_u2f = registrations.iter().any(|r| r.migrated && r.credential.cred_id == rsp.raw_id.0);
@@ -377,7 +386,8 @@ pub fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -
             reg.credential.counter = auth_data.counter;
 
             TwoFactor::new(user_uuid.to_string(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?)
-                .save(conn)?;
+                .save(conn)
+                .await?;
             return Ok(());
         }
     }
diff --git a/src/api/core/two_factor/yubikey.rs b/src/api/core/two_factor/yubikey.rs
index 618c755a..c430d1ed 100644
--- a/src/api/core/two_factor/yubikey.rs
+++ b/src/api/core/two_factor/yubikey.rs
@@ -78,7 +78,7 @@ fn verify_yubikey_otp(otp: String) -> EmptyResult {
 }
 
 #[post("/two-factor/get-yubikey", data = "<data>")]
-fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
     // Make sure the credentials are set
     get_yubico_credentials()?;
 
@@ -92,7 +92,7 @@ fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbCo
     let user_uuid = &user.uuid;
     let yubikey_type = TwoFactorType::YubiKey as i32;
 
-    let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
+    let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn).await;
 
     if let Some(r) = r {
         let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
@@ -113,7 +113,7 @@ fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbCo
 }
 
 #[post("/two-factor/yubikey", data = "<data>")]
-fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
+async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
     let data: EnableYubikeyData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -122,10 +122,11 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
     }
 
     // Check if we already have some data
-    let mut yubikey_data = match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn) {
-        Some(data) => data,
-        None => TwoFactor::new(user.uuid.clone(), TwoFactorType::YubiKey, String::new()),
-    };
+    let mut yubikey_data =
+        match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn).await {
+            Some(data) => data,
+            None => TwoFactor::new(user.uuid.clone(), TwoFactorType::YubiKey, String::new()),
+        };
 
     let yubikeys = parse_yubikeys(&data);
 
@@ -154,9 +155,9 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
     };
 
     yubikey_data.data = serde_json::to_string(&yubikey_metadata).unwrap();
-    yubikey_data.save(&conn)?;
+    yubikey_data.save(&conn).await?;
 
-    _generate_recover_code(&mut user, &conn);
+    _generate_recover_code(&mut user, &conn).await;
 
     let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
 
@@ -168,8 +169,8 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
 }
 
 #[put("/two-factor/yubikey", data = "<data>")]
-fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
-    activate_yubikey(data, headers, conn)
+async fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
+    activate_yubikey(data, headers, conn).await
 }
 
 pub fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult {
diff --git a/src/api/icons.rs b/src/api/icons.rs
index 33656d02..6af10a35 100644
--- a/src/api/icons.rs
+++ b/src/api/icons.rs
@@ -745,6 +745,7 @@ async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> {
                     buffer = stream_to_bytes_limit(res, 512 * 1024).await?; // 512 KB for each icon max
                                                                             // Check if the icon type is allowed, else try an icon from the list.
                     icon_type = get_icon_type(&buffer);
+                    // Check if the icon type is allowed, else try an icon from the list.
                     if icon_type.is_none() {
                         buffer.clear();
                         debug!("Icon from {}, is not a valid image type", icon.href);
diff --git a/src/api/identity.rs b/src/api/identity.rs
index 0ad8a1b5..2c04990b 100644
--- a/src/api/identity.rs
+++ b/src/api/identity.rs
@@ -23,13 +23,13 @@ pub fn routes() -> Vec<Route> {
 }
 
 #[post("/connect/token", data = "<data>")]
-fn login(data: Form<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult {
+async fn login(data: Form<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult {
     let data: ConnectData = data.into_inner();
 
     match data.grant_type.as_ref() {
         "refresh_token" => {
             _check_is_some(&data.refresh_token, "refresh_token cannot be blank")?;
-            _refresh_login(data, conn)
+            _refresh_login(data, conn).await
         }
         "password" => {
             _check_is_some(&data.client_id, "client_id cannot be blank")?;
@@ -41,34 +41,34 @@ fn login(data: Form<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult {
             _check_is_some(&data.device_name, "device_name cannot be blank")?;
             _check_is_some(&data.device_type, "device_type cannot be blank")?;
 
-            _password_login(data, conn, &ip)
+            _password_login(data, conn, &ip).await
         }
         "client_credentials" => {
             _check_is_some(&data.client_id, "client_id cannot be blank")?;
             _check_is_some(&data.client_secret, "client_secret cannot be blank")?;
             _check_is_some(&data.scope, "scope cannot be blank")?;
 
-            _api_key_login(data, conn, &ip)
+            _api_key_login(data, conn, &ip).await
         }
         t => err!("Invalid type", t),
     }
 }
 
-fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
+async fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
     // Extract token
     let token = data.refresh_token.unwrap();
 
     // Get device by refresh token
-    let mut device = Device::find_by_refresh_token(&token, &conn).map_res("Invalid refresh token")?;
+    let mut device = Device::find_by_refresh_token(&token, &conn).await.map_res("Invalid refresh token")?;
 
     let scope = "api offline_access";
     let scope_vec = vec!["api".into(), "offline_access".into()];
 
     // Common
-    let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap();
-    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);
+    let user = User::find_by_uuid(&device.user_uuid, &conn).await.unwrap();
+    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await;
     let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
-    device.save(&conn)?;
+    device.save(&conn).await?;
 
     Ok(Json(json!({
         "access_token": access_token,
@@ -86,7 +86,7 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
     })))
 }
 
-fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
+async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
     // Validate scope
     let scope = data.scope.as_ref().unwrap();
     if scope != "api offline_access" {
@@ -99,7 +99,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
 
     // Get the user
     let username = data.username.as_ref().unwrap();
-    let user = match User::find_by_mail(username, &conn) {
+    let user = match User::find_by_mail(username, &conn).await {
         Some(user) => user,
         None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)),
     };
@@ -130,7 +130,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
                 user.last_verifying_at = Some(now);
                 user.login_verify_count += 1;
 
-                if let Err(e) = user.save(&conn) {
+                if let Err(e) = user.save(&conn).await {
                     error!("Error updating user: {:#?}", e);
                 }
 
@@ -144,9 +144,9 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
         err!("Please verify your email before trying again.", format!("IP: {}. Username: {}.", ip.ip, username))
     }
 
-    let (mut device, new_device) = get_device(&data, &conn, &user);
+    let (mut device, new_device) = get_device(&data, &conn, &user).await;
 
-    let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, &conn)?;
+    let twofactor_token = twofactor_auth(&user.uuid, &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) {
@@ -159,9 +159,9 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
     }
 
     // Common
-    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);
+    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await;
     let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
-    device.save(&conn)?;
+    device.save(&conn).await?;
 
     let mut result = json!({
         "access_token": access_token,
@@ -187,7 +187,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
     Ok(Json(result))
 }
 
-fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
+async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
     // Validate scope
     let scope = data.scope.as_ref().unwrap();
     if scope != "api" {
@@ -204,7 +204,7 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
         Some(uuid) => uuid,
         None => err!("Malformed client_id", format!("IP: {}.", ip.ip)),
     };
-    let user = match User::find_by_uuid(user_uuid, &conn) {
+    let user = match User::find_by_uuid(user_uuid, &conn).await {
         Some(user) => user,
         None => err!("Invalid client_id", format!("IP: {}.", ip.ip)),
     };
@@ -220,7 +220,7 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
         err!("Incorrect client_secret", format!("IP: {}. Username: {}.", ip.ip, user.email))
     }
 
-    let (mut device, new_device) = get_device(&data, &conn, &user);
+    let (mut device, new_device) = get_device(&data, &conn, &user).await;
 
     if CONFIG.mail_enabled() && new_device {
         let now = Utc::now().naive_utc();
@@ -234,9 +234,9 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
     }
 
     // Common
-    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);
+    let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await;
     let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
-    device.save(&conn)?;
+    device.save(&conn).await?;
 
     info!("User {} logged in successfully via API key. IP: {}", user.email, ip.ip);
 
@@ -258,7 +258,7 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
 }
 
 /// Retrieves an existing device or creates a new device from ConnectData and the User
-fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool) {
+async fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool) {
     // On iOS, device_type sends "iOS", on others it sends a number
     let device_type = util::try_parse_string(data.device_type.as_ref()).unwrap_or(0);
     let device_id = data.device_identifier.clone().expect("No device id provided");
@@ -266,7 +266,7 @@ fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool)
 
     let mut new_device = false;
     // Find device or create new
-    let device = match Device::find_by_uuid(&device_id, conn) {
+    let device = match Device::find_by_uuid(&device_id, conn).await {
         Some(device) => {
             // Check if owned device, and recreate if not
             if device.user_uuid != user.uuid {
@@ -286,28 +286,28 @@ fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool)
     (device, new_device)
 }
 
-fn twofactor_auth(
+async fn twofactor_auth(
     user_uuid: &str,
     data: &ConnectData,
     device: &mut Device,
     ip: &ClientIp,
     conn: &DbConn,
 ) -> ApiResult<Option<String>> {
-    let twofactors = TwoFactor::find_by_user(user_uuid, conn);
+    let twofactors = TwoFactor::find_by_user(user_uuid, conn).await;
 
     // No twofactor token if twofactor is disabled
     if twofactors.is_empty() {
         return Ok(None);
     }
 
-    TwoFactorIncomplete::mark_incomplete(user_uuid, &device.uuid, &device.name, ip, conn)?;
+    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, asume 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)?, "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);
@@ -320,16 +320,18 @@ 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)?
+            _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn).await?
+        }
+        Some(TwoFactorType::U2f) => _tf::u2f::validate_u2f_login(user_uuid, twofactor_code, conn).await?,
+        Some(TwoFactorType::Webauthn) => {
+            _tf::webauthn::validate_webauthn_login(user_uuid, twofactor_code, conn).await?
         }
-        Some(TwoFactorType::U2f) => _tf::u2f::validate_u2f_login(user_uuid, twofactor_code, conn)?,
-        Some(TwoFactorType::Webauthn) => _tf::webauthn::validate_webauthn_login(user_uuid, twofactor_code, conn)?,
         Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?)?,
         Some(TwoFactorType::Duo) => {
-            _tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn)?
+            _tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn).await?
         }
         Some(TwoFactorType::Email) => {
-            _tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn)?
+            _tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn).await?
         }
 
         Some(TwoFactorType::Remember) => {
@@ -338,14 +340,17 @@ fn twofactor_auth(
                     remember = 1; // Make sure we also return the token here, otherwise it will only remember the first time
                 }
                 _ => {
-                    err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn)?, "2FA Remember token not provided")
+                    err_json!(
+                        _json_err_twofactor(&twofactor_ids, user_uuid, conn).await?,
+                        "2FA Remember token not provided"
+                    )
                 }
             }
         }
         _ => err!("Invalid two factor provider"),
     }
 
-    TwoFactorIncomplete::mark_complete(user_uuid, &device.uuid, conn)?;
+    TwoFactorIncomplete::mark_complete(user_uuid, &device.uuid, conn).await?;
 
     if !CONFIG.disable_2fa_remember() && remember == 1 {
         Ok(Some(device.refresh_twofactor_remember()))
@@ -359,7 +364,7 @@ fn _selected_data(tf: Option<TwoFactor>) -> ApiResult<String> {
     tf.map(|t| t.data).map_res("Two factor doesn't exist")
 }
 
-fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> ApiResult<Value> {
+async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> ApiResult<Value> {
     use crate::api::core::two_factor;
 
     let mut result = json!({
@@ -376,7 +381,7 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api
             Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ }
 
             Some(TwoFactorType::U2f) if CONFIG.domain_set() => {
-                let request = two_factor::u2f::generate_u2f_login(user_uuid, conn)?;
+                let request = two_factor::u2f::generate_u2f_login(user_uuid, conn).await?;
                 let mut challenge_list = Vec::new();
 
                 for key in request.registered_keys {
@@ -396,17 +401,17 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api
             }
 
             Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => {
-                let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn)?;
+                let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn).await?;
                 result["TwoFactorProviders2"][provider.to_string()] = request.0;
             }
 
             Some(TwoFactorType::Duo) => {
-                let email = match User::find_by_uuid(user_uuid, conn) {
+                let email = match User::find_by_uuid(user_uuid, conn).await {
                     Some(u) => u.email,
                     None => err!("User does not exist"),
                 };
 
-                let (signature, host) = duo::generate_duo_signature(&email, conn)?;
+                let (signature, host) = duo::generate_duo_signature(&email, conn).await?;
 
                 result["TwoFactorProviders2"][provider.to_string()] = json!({
                     "Host": host,
@@ -415,7 +420,7 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api
             }
 
             Some(tf_type @ TwoFactorType::YubiKey) => {
-                let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn) {
+                let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await {
                     Some(tf) => tf,
                     None => err!("No YubiKey devices registered"),
                 };
@@ -430,14 +435,14 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api
             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) {
+                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"),
                 };
 
                 // Send email immediately if email is the only 2FA option
                 if providers.len() == 1 {
-                    _tf::email::send_token(user_uuid, conn)?
+                    _tf::email::send_token(user_uuid, conn).await?
                 }
 
                 let email_data = EmailTokenData::from_json(&twofactor.data)?;
@@ -492,7 +497,7 @@ struct ConnectData {
     device_type: Option<String>,
     #[field(name = uncased("device_push_token"))]
     #[field(name = uncased("devicepushtoken"))]
-    device_push_token: Option<String>, // Unused; mobile device push not yet supported.
+    _device_push_token: Option<String>, // Unused; mobile device push not yet supported.
 
     // Needed for two-factor auth
     #[field(name = uncased("two_factor_provider"))]
diff --git a/src/auth.rs b/src/auth.rs
index 6aedae81..7eaf1494 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -350,12 +350,12 @@ impl<'r> FromRequest<'r> for Headers {
             _ => err_handler!("Error getting DB"),
         };
 
-        let device = match Device::find_by_uuid(&device_uuid, &conn) {
+        let device = match Device::find_by_uuid(&device_uuid, &conn).await {
             Some(device) => device,
             None => err_handler!("Invalid device id"),
         };
 
-        let user = match User::find_by_uuid(&user_uuid, &conn) {
+        let user = match User::find_by_uuid(&user_uuid, &conn).await {
             Some(user) => user,
             None => err_handler!("Device has no user associated"),
         };
@@ -377,7 +377,7 @@ impl<'r> FromRequest<'r> for Headers {
                     // This prevents checking this stamp exception for new requests.
                     let mut user = user;
                     user.reset_stamp_exception();
-                    if let Err(e) = user.save(&conn) {
+                    if let Err(e) = user.save(&conn).await {
                         error!("Error updating user: {:#?}", e);
                     }
                     err_handler!("Stamp exception is expired")
@@ -441,7 +441,7 @@ impl<'r> FromRequest<'r> for OrgHeaders {
                 };
 
                 let user = headers.user;
-                let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn) {
+                let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).await {
                     Some(user) => {
                         if user.status == UserOrgStatus::Confirmed as i32 {
                             user
@@ -553,7 +553,9 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
                     };
 
                     if !headers.org_user.has_full_access() {
-                        match CollectionUser::find_by_collection_and_user(&col_id, &headers.org_user.user_uuid, &conn) {
+                        match CollectionUser::find_by_collection_and_user(&col_id, &headers.org_user.user_uuid, &conn)
+                            .await
+                        {
                             Some(_) => (),
                             None => err_handler!("The current user isn't a manager for this collection"),
                         }
diff --git a/src/db/mod.rs b/src/db/mod.rs
index db52d513..aeceda5a 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -30,7 +30,7 @@ pub mod __mysql_schema;
 #[path = "schemas/postgresql/schema.rs"]
 pub mod __postgresql_schema;
 
-// There changes are based on Rocket 0.5-rc wrapper of Diesel: https://github.com/SergioBenitez/Rocket/blob/v0.5-rc/contrib/sync_db_pools
+// These changes are based on Rocket 0.5-rc wrapper of Diesel: https://github.com/SergioBenitez/Rocket/blob/v0.5-rc/contrib/sync_db_pools
 
 // A wrapper around spawn_blocking that propagates panics to the calling code.
 pub async fn run_blocking<F, R>(job: F) -> R
diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs
index bb0f9395..975687e3 100644
--- a/src/db/models/attachment.rs
+++ b/src/db/models/attachment.rs
@@ -60,7 +60,7 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Attachment {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
         db_run! { conn:
             sqlite, mysql {
                 match diesel::replace_into(attachments::table)
@@ -92,7 +92,7 @@ impl Attachment {
         }
     }
 
-    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             crate::util::retry(
                 || diesel::delete(attachments::table.filter(attachments::id.eq(&self.id))).execute(conn),
@@ -116,14 +116,14 @@ impl Attachment {
         }}
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for attachment in Attachment::find_by_cipher(cipher_uuid, conn) {
-            attachment.delete(conn)?;
+    pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for attachment in Attachment::find_by_cipher(cipher_uuid, conn).await {
+            attachment.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             attachments::table
                 .filter(attachments::id.eq(id.to_lowercase()))
@@ -133,7 +133,7 @@ impl Attachment {
         }}
     }
 
-    pub fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             attachments::table
                 .filter(attachments::cipher_uuid.eq(cipher_uuid))
@@ -143,7 +143,7 @@ impl Attachment {
         }}
     }
 
-    pub fn size_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn size_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
         db_run! { conn: {
             let result: Option<i64> = attachments::table
                 .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@@ -155,7 +155,7 @@ impl Attachment {
         }}
     }
 
-    pub fn count_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn count_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
         db_run! { conn: {
             attachments::table
                 .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@@ -166,7 +166,7 @@ impl Attachment {
         }}
     }
 
-    pub fn size_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn size_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
         db_run! { conn: {
             let result: Option<i64> = attachments::table
                 .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@@ -178,7 +178,7 @@ impl Attachment {
         }}
     }
 
-    pub fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
         db_run! { conn: {
             attachments::table
                 .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs
index 39aaf580..e6f2050b 100644
--- a/src/db/models/cipher.rs
+++ b/src/db/models/cipher.rs
@@ -82,10 +82,10 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Cipher {
-    pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
+    pub async fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
         use crate::util::format_date;
 
-        let attachments = Attachment::find_by_cipher(&self.uuid, conn);
+        let attachments = Attachment::find_by_cipher(&self.uuid, conn).await;
         // When there are no attachments use null instead of an empty array
         let attachments_json = if attachments.is_empty() {
             Value::Null
@@ -97,7 +97,7 @@ impl Cipher {
         let password_history_json =
             self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
 
-        let (read_only, hide_passwords) = match self.get_access_restrictions(user_uuid, conn) {
+        let (read_only, hide_passwords) = match self.get_access_restrictions(user_uuid, conn).await {
             Some((ro, hp)) => (ro, hp),
             None => {
                 error!("Cipher ownership assertion failure");
@@ -144,8 +144,8 @@ impl Cipher {
             "Type": self.atype,
             "RevisionDate": format_date(&self.updated_at),
             "DeletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))),
-            "FolderId": self.get_folder_uuid(user_uuid, conn),
-            "Favorite": self.is_favorite(user_uuid, conn),
+            "FolderId": self.get_folder_uuid(user_uuid, conn).await,
+            "Favorite": self.is_favorite(user_uuid, conn).await,
             "Reprompt": self.reprompt.unwrap_or(RepromptType::None as i32),
             "OrganizationId": self.organization_uuid,
             "Attachments": attachments_json,
@@ -154,7 +154,7 @@ impl Cipher {
             "OrganizationUseTotp": true,
 
             // This field is specific to the cipherDetails type.
-            "CollectionIds": self.get_collections(user_uuid, conn),
+            "CollectionIds": self.get_collections(user_uuid, conn).await,
 
             "Name": self.name,
             "Notes": self.notes,
@@ -189,28 +189,28 @@ impl Cipher {
         json_object
     }
 
-    pub fn update_users_revision(&self, conn: &DbConn) -> Vec<String> {
+    pub async fn update_users_revision(&self, conn: &DbConn) -> Vec<String> {
         let mut user_uuids = Vec::new();
         match self.user_uuid {
             Some(ref user_uuid) => {
-                User::update_uuid_revision(user_uuid, conn);
+                User::update_uuid_revision(user_uuid, conn).await;
                 user_uuids.push(user_uuid.clone())
             }
             None => {
                 // Belongs to Organization, need to update affected users
                 if let Some(ref org_uuid) = self.organization_uuid {
-                    UserOrganization::find_by_cipher_and_org(&self.uuid, org_uuid, conn).iter().for_each(|user_org| {
-                        User::update_uuid_revision(&user_org.user_uuid, conn);
+                    for user_org in UserOrganization::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await.iter() {
+                        User::update_uuid_revision(&user_org.user_uuid, conn).await;
                         user_uuids.push(user_org.user_uuid.clone())
-                    });
+                    }
                 }
             }
         };
         user_uuids
     }
 
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
         self.updated_at = Utc::now().naive_utc();
 
         db_run! { conn:
@@ -244,13 +244,13 @@ impl Cipher {
         }
     }
 
-    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
+    pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
 
-        FolderCipher::delete_all_by_cipher(&self.uuid, conn)?;
-        CollectionCipher::delete_all_by_cipher(&self.uuid, conn)?;
-        Attachment::delete_all_by_cipher(&self.uuid, conn)?;
-        Favorite::delete_all_by_cipher(&self.uuid, conn)?;
+        FolderCipher::delete_all_by_cipher(&self.uuid, conn).await?;
+        CollectionCipher::delete_all_by_cipher(&self.uuid, conn).await?;
+        Attachment::delete_all_by_cipher(&self.uuid, conn).await?;
+        Favorite::delete_all_by_cipher(&self.uuid, conn).await?;
 
         db_run! { conn: {
             diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
@@ -259,54 +259,55 @@ impl Cipher {
         }}
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for cipher in Self::find_by_org(org_uuid, conn) {
-            cipher.delete(conn)?;
+    pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
+        // TODO: Optimize this by executing a DELETE directly on the database, instead of first fetching.
+        for cipher in Self::find_by_org(org_uuid, conn).await {
+            cipher.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for cipher in Self::find_owned_by_user(user_uuid, conn) {
-            cipher.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for cipher in Self::find_owned_by_user(user_uuid, conn).await {
+            cipher.delete(conn).await?;
         }
         Ok(())
     }
 
     /// Purge all ciphers that are old enough to be auto-deleted.
-    pub fn purge_trash(conn: &DbConn) {
+    pub async fn purge_trash(conn: &DbConn) {
         if let Some(auto_delete_days) = CONFIG.trash_auto_delete_days() {
             let now = Utc::now().naive_utc();
             let dt = now - Duration::days(auto_delete_days);
-            for cipher in Self::find_deleted_before(&dt, conn) {
-                cipher.delete(conn).ok();
+            for cipher in Self::find_deleted_before(&dt, conn).await {
+                cipher.delete(conn).await.ok();
             }
         }
     }
 
-    pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(user_uuid, conn);
+    pub async fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(user_uuid, conn).await;
 
-        match (self.get_folder_uuid(user_uuid, conn), folder_uuid) {
+        match (self.get_folder_uuid(user_uuid, conn).await, folder_uuid) {
             // No changes
             (None, None) => Ok(()),
             (Some(ref old), Some(ref new)) if old == new => Ok(()),
 
             // Add to folder
-            (None, Some(new)) => FolderCipher::new(&new, &self.uuid).save(conn),
+            (None, Some(new)) => FolderCipher::new(&new, &self.uuid).save(conn).await,
 
             // Remove from folder
-            (Some(old), None) => match FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn) {
-                Some(old) => old.delete(conn),
+            (Some(old), None) => match FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn).await {
+                Some(old) => old.delete(conn).await,
                 None => err!("Couldn't move from previous folder"),
             },
 
             // Move to another folder
             (Some(old), Some(new)) => {
-                if let Some(old) = FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn) {
-                    old.delete(conn)?;
+                if let Some(old) = FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, conn).await {
+                    old.delete(conn).await?;
                 }
-                FolderCipher::new(&new, &self.uuid).save(conn)
+                FolderCipher::new(&new, &self.uuid).save(conn).await
             }
         }
     }
@@ -317,9 +318,9 @@ impl Cipher {
     }
 
     /// Returns whether this cipher is owned by an org in which the user has full access.
-    pub fn is_in_full_access_org(&self, user_uuid: &str, conn: &DbConn) -> bool {
+    pub async fn is_in_full_access_org(&self, user_uuid: &str, conn: &DbConn) -> bool {
         if let Some(ref org_uuid) = self.organization_uuid {
-            if let Some(user_org) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {
+            if let Some(user_org) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await {
                 return user_org.has_full_access();
             }
         }
@@ -332,11 +333,11 @@ impl Cipher {
     /// not in any collection the user has access to. Otherwise, the user has
     /// access to this cipher, and Some(read_only, hide_passwords) represents
     /// the access restrictions.
-    pub fn get_access_restrictions(&self, user_uuid: &str, conn: &DbConn) -> Option<(bool, bool)> {
+    pub async fn get_access_restrictions(&self, user_uuid: &str, conn: &DbConn) -> Option<(bool, bool)> {
         // Check whether this cipher is directly owned by the user, or is in
         // a collection that the user has full access to. If so, there are no
         // access restrictions.
-        if self.is_owned_by_user(user_uuid) || self.is_in_full_access_org(user_uuid, conn) {
+        if self.is_owned_by_user(user_uuid) || self.is_in_full_access_org(user_uuid, conn).await {
             return Some((false, false));
         }
 
@@ -379,31 +380,31 @@ impl Cipher {
         }}
     }
 
-    pub fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
-        match self.get_access_restrictions(user_uuid, conn) {
+    pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
+        match self.get_access_restrictions(user_uuid, conn).await {
             Some((read_only, _hide_passwords)) => !read_only,
             None => false,
         }
     }
 
-    pub fn is_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
-        self.get_access_restrictions(user_uuid, conn).is_some()
+    pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
+        self.get_access_restrictions(user_uuid, conn).await.is_some()
     }
 
     // Returns whether this cipher is a favorite of the specified user.
-    pub fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool {
-        Favorite::is_favorite(&self.uuid, user_uuid, conn)
+    pub async fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool {
+        Favorite::is_favorite(&self.uuid, user_uuid, conn).await
     }
 
     // Sets whether this cipher is a favorite of the specified user.
-    pub fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
         match favorite {
             None => Ok(()), // No change requested.
-            Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn),
+            Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn).await,
         }
     }
 
-    pub fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option<String> {
+    pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option<String> {
         db_run! {conn: {
             folders_ciphers::table
                 .inner_join(folders::table)
@@ -415,7 +416,7 @@ impl Cipher {
         }}
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! {conn: {
             ciphers::table
                 .filter(ciphers::uuid.eq(uuid))
@@ -437,7 +438,7 @@ impl Cipher {
     // true, then the non-interesting ciphers will not be returned. As a
     // result, those ciphers will not appear in "My Vault" for the org
     // owner/admin, but they can still be accessed via the org vault view.
-    pub fn find_by_user(user_uuid: &str, visible_only: bool, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             let mut query = ciphers::table
                 .left_join(ciphers_collections::table.on(
@@ -472,12 +473,12 @@ impl Cipher {
     }
 
     // Find all ciphers visible to the specified user.
-    pub fn find_by_user_visible(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
-        Self::find_by_user(user_uuid, true, conn)
+    pub async fn find_by_user_visible(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+        Self::find_by_user(user_uuid, true, conn).await
     }
 
     // Find all ciphers directly owned by the specified user.
-    pub fn find_owned_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_owned_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             ciphers::table
                 .filter(
@@ -488,7 +489,7 @@ impl Cipher {
         }}
     }
 
-    pub fn count_owned_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn count_owned_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
         db_run! {conn: {
             ciphers::table
                 .filter(ciphers::user_uuid.eq(user_uuid))
@@ -499,7 +500,7 @@ impl Cipher {
         }}
     }
 
-    pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             ciphers::table
                 .filter(ciphers::organization_uuid.eq(org_uuid))
@@ -507,7 +508,7 @@ impl Cipher {
         }}
     }
 
-    pub fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
         db_run! {conn: {
             ciphers::table
                 .filter(ciphers::organization_uuid.eq(org_uuid))
@@ -518,7 +519,7 @@ impl Cipher {
         }}
     }
 
-    pub fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             folders_ciphers::table.inner_join(ciphers::table)
                 .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
@@ -528,7 +529,7 @@ impl Cipher {
     }
 
     /// Find all ciphers that were deleted before the specified datetime.
-    pub fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             ciphers::table
                 .filter(ciphers::deleted_at.lt(dt))
@@ -536,7 +537,7 @@ impl Cipher {
         }}
     }
 
-    pub fn get_collections(&self, user_id: &str, conn: &DbConn) -> Vec<String> {
+    pub async fn get_collections(&self, user_id: &str, conn: &DbConn) -> Vec<String> {
         db_run! {conn: {
             ciphers_collections::table
             .inner_join(collections::table.on(
diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs
index 2073ca17..b5782f7b 100644
--- a/src/db/models/collection.rs
+++ b/src/db/models/collection.rs
@@ -57,11 +57,11 @@ impl Collection {
         })
     }
 
-    pub fn to_json_details(&self, user_uuid: &str, conn: &DbConn) -> Value {
+    pub async fn to_json_details(&self, user_uuid: &str, conn: &DbConn) -> Value {
         let mut json_object = self.to_json();
         json_object["Object"] = json!("collectionDetails");
-        json_object["ReadOnly"] = json!(!self.is_writable_by_user(user_uuid, conn));
-        json_object["HidePasswords"] = json!(self.hide_passwords_for_user(user_uuid, conn));
+        json_object["ReadOnly"] = json!(!self.is_writable_by_user(user_uuid, conn).await);
+        json_object["HidePasswords"] = json!(self.hide_passwords_for_user(user_uuid, conn).await);
         json_object
     }
 }
@@ -73,8 +73,8 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Collection {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
 
         db_run! { conn:
             sqlite, mysql {
@@ -107,10 +107,10 @@ impl Collection {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
-        CollectionCipher::delete_all_by_collection(&self.uuid, conn)?;
-        CollectionUser::delete_all_by_collection(&self.uuid, conn)?;
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
+        CollectionCipher::delete_all_by_collection(&self.uuid, conn).await?;
+        CollectionUser::delete_all_by_collection(&self.uuid, conn).await?;
 
         db_run! { conn: {
             diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid)))
@@ -119,20 +119,20 @@ impl Collection {
         }}
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for collection in Self::find_by_organization(org_uuid, conn) {
-            collection.delete(conn)?;
+    pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for collection in Self::find_by_organization(org_uuid, conn).await {
+            collection.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn update_users_revision(&self, conn: &DbConn) {
-        UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).iter().for_each(|user_org| {
-            User::update_uuid_revision(&user_org.user_uuid, conn);
-        });
+    pub async fn update_users_revision(&self, conn: &DbConn) {
+        for user_org in UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).await.iter() {
+            User::update_uuid_revision(&user_org.user_uuid, conn).await;
+        }
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             collections::table
                 .filter(collections::uuid.eq(uuid))
@@ -142,7 +142,7 @@ impl Collection {
         }}
     }
 
-    pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             collections::table
             .left_join(users_collections::table.on(
@@ -167,11 +167,11 @@ impl Collection {
         }}
     }
 
-    pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> {
-        Self::find_by_user_uuid(user_uuid, conn).into_iter().filter(|c| c.org_uuid == org_uuid).collect()
+    pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+        Self::find_by_user_uuid(user_uuid, conn).await.into_iter().filter(|c| c.org_uuid == org_uuid).collect()
     }
 
-    pub fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             collections::table
                 .filter(collections::org_uuid.eq(org_uuid))
@@ -181,7 +181,7 @@ impl Collection {
         }}
     }
 
-    pub fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             collections::table
                 .filter(collections::uuid.eq(uuid))
@@ -193,7 +193,7 @@ impl Collection {
         }}
     }
 
-    pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             collections::table
             .left_join(users_collections::table.on(
@@ -219,8 +219,8 @@ impl Collection {
         }}
     }
 
-    pub fn is_writable_by_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
-        match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn) {
+    pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
+        match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await {
             None => false, // Not in Org
             Some(user_org) => {
                 if user_org.has_full_access() {
@@ -241,8 +241,8 @@ impl Collection {
         }
     }
 
-    pub fn hide_passwords_for_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
-        match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn) {
+    pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
+        match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await {
             None => true, // Not in Org
             Some(user_org) => {
                 if user_org.has_full_access() {
@@ -266,7 +266,7 @@ impl Collection {
 
 /// Database methods
 impl CollectionUser {
-    pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_collections::table
                 .filter(users_collections::user_uuid.eq(user_uuid))
@@ -279,14 +279,14 @@ impl CollectionUser {
         }}
     }
 
-    pub fn save(
+    pub async fn save(
         user_uuid: &str,
         collection_uuid: &str,
         read_only: bool,
         hide_passwords: bool,
         conn: &DbConn,
     ) -> EmptyResult {
-        User::update_uuid_revision(user_uuid, conn);
+        User::update_uuid_revision(user_uuid, conn).await;
 
         db_run! { conn:
             sqlite, mysql {
@@ -337,8 +337,8 @@ impl CollectionUser {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.user_uuid, conn);
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.user_uuid, conn).await;
 
         db_run! { conn: {
             diesel::delete(
@@ -351,7 +351,7 @@ impl CollectionUser {
         }}
     }
 
-    pub fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_collections::table
                 .filter(users_collections::collection_uuid.eq(collection_uuid))
@@ -362,7 +362,7 @@ impl CollectionUser {
         }}
     }
 
-    pub fn find_by_collection_and_user(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_collection_and_user(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             users_collections::table
                 .filter(users_collections::collection_uuid.eq(collection_uuid))
@@ -374,10 +374,10 @@ impl CollectionUser {
         }}
     }
 
-    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
-        CollectionUser::find_by_collection(collection_uuid, conn).iter().for_each(|collection| {
-            User::update_uuid_revision(&collection.user_uuid, conn);
-        });
+    pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for collection in CollectionUser::find_by_collection(collection_uuid, conn).await.iter() {
+            User::update_uuid_revision(&collection.user_uuid, conn).await;
+        }
 
         db_run! { conn: {
             diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid)))
@@ -386,8 +386,8 @@ impl CollectionUser {
         }}
     }
 
-    pub fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> EmptyResult {
-        let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn);
+    pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> EmptyResult {
+        let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await;
 
         db_run! { conn: {
             for user in collectionusers {
@@ -405,8 +405,8 @@ impl CollectionUser {
 
 /// Database methods
 impl CollectionCipher {
-    pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
-        Self::update_users_revision(collection_uuid, conn);
+    pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
+        Self::update_users_revision(collection_uuid, conn).await;
 
         db_run! { conn:
             sqlite, mysql {
@@ -435,8 +435,8 @@ impl CollectionCipher {
         }
     }
 
-    pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
-        Self::update_users_revision(collection_uuid, conn);
+    pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
+        Self::update_users_revision(collection_uuid, conn).await;
 
         db_run! { conn: {
             diesel::delete(
@@ -449,7 +449,7 @@ impl CollectionCipher {
         }}
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)))
                 .execute(conn)
@@ -457,7 +457,7 @@ impl CollectionCipher {
         }}
     }
 
-    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid)))
                 .execute(conn)
@@ -465,9 +465,9 @@ impl CollectionCipher {
         }}
     }
 
-    pub fn update_users_revision(collection_uuid: &str, conn: &DbConn) {
-        if let Some(collection) = Collection::find_by_uuid(collection_uuid, conn) {
-            collection.update_users_revision(conn);
+    pub async fn update_users_revision(collection_uuid: &str, conn: &DbConn) {
+        if let Some(collection) = Collection::find_by_uuid(collection_uuid, conn).await {
+            collection.update_users_revision(conn).await;
         }
     }
 }
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
index 05955c04..2f5cd018 100644
--- a/src/db/models/device.rs
+++ b/src/db/models/device.rs
@@ -118,7 +118,7 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Device {
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
         self.updated_at = Utc::now().naive_utc();
 
         db_run! { conn:
@@ -138,7 +138,7 @@ impl Device {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(devices::table.filter(devices::uuid.eq(self.uuid)))
                 .execute(conn)
@@ -146,14 +146,14 @@ impl Device {
         }}
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for device in Self::find_by_user(user_uuid, conn) {
-            device.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for device in Self::find_by_user(user_uuid, conn).await {
+            device.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             devices::table
                 .filter(devices::uuid.eq(uuid))
@@ -163,7 +163,7 @@ impl Device {
         }}
     }
 
-    pub fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             devices::table
                 .filter(devices::refresh_token.eq(refresh_token))
@@ -173,7 +173,7 @@ impl Device {
         }}
     }
 
-    pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             devices::table
                 .filter(devices::user_uuid.eq(user_uuid))
@@ -183,7 +183,7 @@ impl Device {
         }}
     }
 
-    pub fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             devices::table
                 .filter(devices::user_uuid.eq(user_uuid))
diff --git a/src/db/models/emergency_access.rs b/src/db/models/emergency_access.rs
index 7327eb34..e878507b 100644
--- a/src/db/models/emergency_access.rs
+++ b/src/db/models/emergency_access.rs
@@ -73,8 +73,8 @@ impl EmergencyAccess {
         })
     }
 
-    pub fn to_json_grantor_details(&self, conn: &DbConn) -> Value {
-        let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).expect("Grantor user not found.");
+    pub async fn to_json_grantor_details(&self, conn: &DbConn) -> Value {
+        let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).await.expect("Grantor user not found.");
 
         json!({
             "Id": self.uuid,
@@ -89,11 +89,11 @@ impl EmergencyAccess {
     }
 
     #[allow(clippy::manual_map)]
-    pub fn to_json_grantee_details(&self, conn: &DbConn) -> Value {
+    pub async fn to_json_grantee_details(&self, conn: &DbConn) -> Value {
         let grantee_user = if let Some(grantee_uuid) = self.grantee_uuid.as_deref() {
-            Some(User::find_by_uuid(grantee_uuid, conn).expect("Grantee user not found."))
+            Some(User::find_by_uuid(grantee_uuid, conn).await.expect("Grantee user not found."))
         } else if let Some(email) = self.email.as_deref() {
-            Some(User::find_by_mail(email, conn).expect("Grantee user not found."))
+            Some(User::find_by_mail(email, conn).await.expect("Grantee user not found."))
         } else {
             None
         };
@@ -155,8 +155,8 @@ use crate::api::EmptyResult;
 use crate::error::MapResult;
 
 impl EmergencyAccess {
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.grantor_uuid, conn);
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.grantor_uuid, conn).await;
         self.updated_at = Utc::now().naive_utc();
 
         db_run! { conn:
@@ -190,18 +190,18 @@ impl EmergencyAccess {
         }
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for ea in Self::find_all_by_grantor_uuid(user_uuid, conn) {
-            ea.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for ea in Self::find_all_by_grantor_uuid(user_uuid, conn).await {
+            ea.delete(conn).await?;
         }
-        for ea in Self::find_all_by_grantee_uuid(user_uuid, conn) {
-            ea.delete(conn)?;
+        for ea in Self::find_all_by_grantee_uuid(user_uuid, conn).await {
+            ea.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.grantor_uuid, conn);
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.grantor_uuid, conn).await;
 
         db_run! { conn: {
             diesel::delete(emergency_access::table.filter(emergency_access::uuid.eq(self.uuid)))
@@ -210,7 +210,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::uuid.eq(uuid))
@@ -219,7 +219,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_by_grantor_uuid_and_grantee_uuid_or_email(
+    pub async fn find_by_grantor_uuid_and_grantee_uuid_or_email(
         grantor_uuid: &str,
         grantee_uuid: &str,
         email: &str,
@@ -234,7 +234,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_all_recoveries(conn: &DbConn) -> Vec<Self> {
+    pub async fn find_all_recoveries(conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32))
@@ -242,7 +242,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::uuid.eq(uuid))
@@ -252,7 +252,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::grantee_uuid.eq(grantee_uuid))
@@ -260,7 +260,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::email.eq(grantee_email))
@@ -270,7 +270,7 @@ impl EmergencyAccess {
         }}
     }
 
-    pub fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             emergency_access::table
                 .filter(emergency_access::grantor_uuid.eq(grantor_uuid))
diff --git a/src/db/models/favorite.rs b/src/db/models/favorite.rs
index cb3e3420..4ff31939 100644
--- a/src/db/models/favorite.rs
+++ b/src/db/models/favorite.rs
@@ -19,7 +19,7 @@ use crate::error::MapResult;
 
 impl Favorite {
     // Returns whether the specified cipher is a favorite of the specified user.
-    pub fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool {
+    pub async fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool {
         db_run! { conn: {
             let query = favorites::table
                 .filter(favorites::cipher_uuid.eq(cipher_uuid))
@@ -31,11 +31,11 @@ impl Favorite {
     }
 
     // Sets whether the specified cipher is a favorite of the specified user.
-    pub fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn), favorite);
+    pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite);
         match (old, new) {
             (false, true) => {
-                User::update_uuid_revision(user_uuid, conn);
+                User::update_uuid_revision(user_uuid, conn).await;
                 db_run! { conn: {
                 diesel::insert_into(favorites::table)
                     .values((
@@ -47,7 +47,7 @@ impl Favorite {
                 }}
             }
             (true, false) => {
-                User::update_uuid_revision(user_uuid, conn);
+                User::update_uuid_revision(user_uuid, conn).await;
                 db_run! { conn: {
                     diesel::delete(
                         favorites::table
@@ -64,7 +64,7 @@ impl Favorite {
     }
 
     // Delete all favorite entries associated with the specified cipher.
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid)))
                 .execute(conn)
@@ -73,7 +73,7 @@ impl Favorite {
     }
 
     // Delete all favorite entries associated with the specified user.
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid)))
                 .execute(conn)
diff --git a/src/db/models/folder.rs b/src/db/models/folder.rs
index d51e71b4..33976203 100644
--- a/src/db/models/folder.rs
+++ b/src/db/models/folder.rs
@@ -70,8 +70,8 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Folder {
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.user_uuid, conn);
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.user_uuid, conn).await;
         self.updated_at = Utc::now().naive_utc();
 
         db_run! { conn:
@@ -105,9 +105,9 @@ impl Folder {
         }
     }
 
-    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.user_uuid, conn);
-        FolderCipher::delete_all_by_folder(&self.uuid, conn)?;
+    pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.user_uuid, conn).await;
+        FolderCipher::delete_all_by_folder(&self.uuid, conn).await?;
 
         db_run! { conn: {
             diesel::delete(folders::table.filter(folders::uuid.eq(&self.uuid)))
@@ -116,14 +116,14 @@ impl Folder {
         }}
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for folder in Self::find_by_user(user_uuid, conn) {
-            folder.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for folder in Self::find_by_user(user_uuid, conn).await {
+            folder.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             folders::table
                 .filter(folders::uuid.eq(uuid))
@@ -133,7 +133,7 @@ impl Folder {
         }}
     }
 
-    pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             folders::table
                 .filter(folders::user_uuid.eq(user_uuid))
@@ -145,7 +145,7 @@ impl Folder {
 }
 
 impl FolderCipher {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
         db_run! { conn:
             sqlite, mysql {
                 // Not checking for ForeignKey Constraints here.
@@ -167,7 +167,7 @@ impl FolderCipher {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(
                 folders_ciphers::table
@@ -179,7 +179,7 @@ impl FolderCipher {
         }}
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)))
                 .execute(conn)
@@ -187,7 +187,7 @@ impl FolderCipher {
         }}
     }
 
-    pub fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid)))
                 .execute(conn)
@@ -195,7 +195,7 @@ impl FolderCipher {
         }}
     }
 
-    pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             folders_ciphers::table
                 .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
@@ -206,7 +206,7 @@ impl FolderCipher {
         }}
     }
 
-    pub fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             folders_ciphers::table
                 .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs
index 7c6cefd3..04fc6f45 100644
--- a/src/db/models/org_policy.rs
+++ b/src/db/models/org_policy.rs
@@ -72,7 +72,7 @@ impl OrgPolicy {
 
 /// Database methods
 impl OrgPolicy {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
         db_run! { conn:
             sqlite, mysql {
                 match diesel::replace_into(org_policies::table)
@@ -115,7 +115,7 @@ impl OrgPolicy {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid)))
                 .execute(conn)
@@ -123,7 +123,7 @@ impl OrgPolicy {
         }}
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             org_policies::table
                 .filter(org_policies::uuid.eq(uuid))
@@ -133,7 +133,7 @@ impl OrgPolicy {
         }}
     }
 
-    pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             org_policies::table
                 .filter(org_policies::org_uuid.eq(org_uuid))
@@ -143,7 +143,7 @@ impl OrgPolicy {
         }}
     }
 
-    pub fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             org_policies::table
                 .inner_join(
@@ -161,7 +161,7 @@ impl OrgPolicy {
         }}
     }
 
-    pub fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             org_policies::table
                 .filter(org_policies::org_uuid.eq(org_uuid))
@@ -172,7 +172,7 @@ impl OrgPolicy {
         }}
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid)))
                 .execute(conn)
@@ -183,12 +183,12 @@ impl OrgPolicy {
     /// Returns true if the user belongs to an org that has enabled the specified policy type,
     /// and the user is not an owner or admin of that org. This is only useful for checking
     /// applicability of policy types that have these particular semantics.
-    pub fn is_applicable_to_user(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> bool {
+    pub async fn is_applicable_to_user(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> bool {
         // TODO: Should check confirmed and accepted users
-        for policy in OrgPolicy::find_confirmed_by_user(user_uuid, conn) {
+        for policy in OrgPolicy::find_confirmed_by_user(user_uuid, conn).await {
             if policy.enabled && policy.has_type(policy_type) {
                 let org_uuid = &policy.org_uuid;
-                if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {
+                if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await {
                     if user.atype < UserOrgType::Admin {
                         return true;
                     }
@@ -200,11 +200,11 @@ impl OrgPolicy {
 
     /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail`
     /// option of the `Send Options` policy, and the user is not an owner or admin of that org.
-    pub fn is_hide_email_disabled(user_uuid: &str, conn: &DbConn) -> bool {
-        for policy in OrgPolicy::find_confirmed_by_user(user_uuid, conn) {
+    pub async fn is_hide_email_disabled(user_uuid: &str, conn: &DbConn) -> bool {
+        for policy in OrgPolicy::find_confirmed_by_user(user_uuid, conn).await {
             if policy.enabled && policy.has_type(OrgPolicyType::SendOptions) {
                 let org_uuid = &policy.org_uuid;
-                if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {
+                if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await {
                     if user.atype < UserOrgType::Admin {
                         match serde_json::from_str::<UpCase<SendOptionsPolicyData>>(&policy.data) {
                             Ok(opts) => {
@@ -220,12 +220,4 @@ impl OrgPolicy {
         }
         false
     }
-
-    /*pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        db_run! { conn: {
-            diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid)))
-                .execute(conn)
-                .map_res("Error deleting twofactors")
-        }}
-    }*/
 }
diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs
index 67dd5357..56af0c47 100644
--- a/src/db/models/organization.rs
+++ b/src/db/models/organization.rs
@@ -193,10 +193,10 @@ use crate::error::MapResult;
 
 /// Database methods
 impl Organization {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
-        UserOrganization::find_by_org(&self.uuid, conn).iter().for_each(|user_org| {
-            User::update_uuid_revision(&user_org.user_uuid, conn);
-        });
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
+        for user_org in UserOrganization::find_by_org(&self.uuid, conn).await.iter() {
+            User::update_uuid_revision(&user_org.user_uuid, conn).await;
+        }
 
         db_run! { conn:
             sqlite, mysql {
@@ -230,13 +230,13 @@ impl Organization {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         use super::{Cipher, Collection};
 
-        Cipher::delete_all_by_organization(&self.uuid, conn)?;
-        Collection::delete_all_by_organization(&self.uuid, conn)?;
-        UserOrganization::delete_all_by_organization(&self.uuid, conn)?;
-        OrgPolicy::delete_all_by_organization(&self.uuid, conn)?;
+        Cipher::delete_all_by_organization(&self.uuid, conn).await?;
+        Collection::delete_all_by_organization(&self.uuid, conn).await?;
+        UserOrganization::delete_all_by_organization(&self.uuid, conn).await?;
+        OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?;
 
         db_run! { conn: {
             diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid)))
@@ -245,7 +245,7 @@ impl Organization {
         }}
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             organizations::table
                 .filter(organizations::uuid.eq(uuid))
@@ -254,7 +254,7 @@ impl Organization {
         }}
     }
 
-    pub fn get_all(conn: &DbConn) -> Vec<Self> {
+    pub async fn get_all(conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             organizations::table.load::<OrganizationDb>(conn).expect("Error loading organizations").from_db()
         }}
@@ -262,8 +262,8 @@ impl Organization {
 }
 
 impl UserOrganization {
-    pub fn to_json(&self, conn: &DbConn) -> Value {
-        let org = Organization::find_by_uuid(&self.org_uuid, conn).unwrap();
+    pub async fn to_json(&self, conn: &DbConn) -> Value {
+        let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap();
 
         json!({
             "Id": self.org_uuid,
@@ -322,8 +322,8 @@ impl UserOrganization {
         })
     }
 
-    pub fn to_json_user_details(&self, conn: &DbConn) -> Value {
-        let user = User::find_by_uuid(&self.user_uuid, conn).unwrap();
+    pub async fn to_json_user_details(&self, conn: &DbConn) -> Value {
+        let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap();
 
         json!({
             "Id": self.uuid,
@@ -347,11 +347,12 @@ impl UserOrganization {
         })
     }
 
-    pub fn to_json_details(&self, conn: &DbConn) -> Value {
+    pub async fn to_json_details(&self, conn: &DbConn) -> Value {
         let coll_uuids = if self.access_all {
             vec![] // If we have complete access, no need to fill the array
         } else {
-            let collections = CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn);
+            let collections =
+                CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn).await;
             collections
                 .iter()
                 .map(|c| {
@@ -376,8 +377,8 @@ impl UserOrganization {
             "Object": "organizationUserDetails",
         })
     }
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.user_uuid, conn);
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.user_uuid, conn).await;
 
         db_run! { conn:
             sqlite, mysql {
@@ -410,10 +411,10 @@ impl UserOrganization {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        User::update_uuid_revision(&self.user_uuid, conn);
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        User::update_uuid_revision(&self.user_uuid, conn).await;
 
-        CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn)?;
+        CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?;
 
         db_run! { conn: {
             diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid)))
@@ -422,23 +423,23 @@ impl UserOrganization {
         }}
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for user_org in Self::find_by_org(org_uuid, conn) {
-            user_org.delete(conn)?;
+    pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for user_org in Self::find_by_org(org_uuid, conn).await {
+            user_org.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for user_org in Self::find_any_state_by_user(user_uuid, conn) {
-            user_org.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for user_org in Self::find_any_state_by_user(user_uuid, conn).await {
+            user_org.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn find_by_email_and_org(email: &str, org_id: &str, conn: &DbConn) -> Option<UserOrganization> {
-        if let Some(user) = super::User::find_by_mail(email, conn) {
-            if let Some(user_org) = UserOrganization::find_by_user_and_org(&user.uuid, org_id, conn) {
+    pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &DbConn) -> Option<UserOrganization> {
+        if let Some(user) = super::User::find_by_mail(email, conn).await {
+            if let Some(user_org) = UserOrganization::find_by_user_and_org(&user.uuid, org_id, conn).await {
                 return Some(user_org);
             }
         }
@@ -458,7 +459,7 @@ impl UserOrganization {
         (self.access_all || self.atype >= UserOrgType::Admin) && self.has_status(UserOrgStatus::Confirmed)
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::uuid.eq(uuid))
@@ -467,7 +468,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::uuid.eq(uuid))
@@ -477,7 +478,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::user_uuid.eq(user_uuid))
@@ -487,7 +488,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_invited_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_invited_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::user_uuid.eq(user_uuid))
@@ -497,7 +498,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_any_state_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_any_state_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::user_uuid.eq(user_uuid))
@@ -506,7 +507,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::org_uuid.eq(org_uuid))
@@ -515,7 +516,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
+    pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::org_uuid.eq(org_uuid))
@@ -526,7 +527,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::org_uuid.eq(org_uuid))
@@ -536,7 +537,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             users_organizations::table
                 .filter(users_organizations::user_uuid.eq(user_uuid))
@@ -546,7 +547,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
                 .inner_join(
@@ -565,7 +566,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
             .filter(users_organizations::org_uuid.eq(org_uuid))
@@ -587,7 +588,7 @@ impl UserOrganization {
         }}
     }
 
-    pub fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             users_organizations::table
             .filter(users_organizations::org_uuid.eq(org_uuid))
diff --git a/src/db/models/send.rs b/src/db/models/send.rs
index 9cfb7b1e..cc8fd4fa 100644
--- a/src/db/models/send.rs
+++ b/src/db/models/send.rs
@@ -47,7 +47,7 @@ pub enum SendType {
 }
 
 impl Send {
-    pub fn new(atype: i32, name: String, data: String, akey: String, deletion_date: NaiveDateTime) -> Self {
+    pub async fn new(atype: i32, name: String, data: String, akey: String, deletion_date: NaiveDateTime) -> Self {
         let now = Utc::now().naive_utc();
 
         Self {
@@ -103,7 +103,7 @@ impl Send {
         }
     }
 
-    pub fn creator_identifier(&self, conn: &DbConn) -> Option<String> {
+    pub async fn creator_identifier(&self, conn: &DbConn) -> Option<String> {
         if let Some(hide_email) = self.hide_email {
             if hide_email {
                 return None;
@@ -111,7 +111,7 @@ impl Send {
         }
 
         if let Some(user_uuid) = &self.user_uuid {
-            if let Some(user) = User::find_by_uuid(user_uuid, conn) {
+            if let Some(user) = User::find_by_uuid(user_uuid, conn).await {
                 return Some(user.email);
             }
         }
@@ -150,7 +150,7 @@ impl Send {
         })
     }
 
-    pub fn to_json_access(&self, conn: &DbConn) -> Value {
+    pub async fn to_json_access(&self, conn: &DbConn) -> Value {
         use crate::util::format_date;
 
         let data: Value = serde_json::from_str(&self.data).unwrap_or_default();
@@ -164,7 +164,7 @@ impl Send {
             "File": if self.atype == SendType::File as i32 { Some(&data) } else { None },
 
             "ExpirationDate": self.expiration_date.as_ref().map(format_date),
-            "CreatorIdentifier": self.creator_identifier(conn),
+            "CreatorIdentifier": self.creator_identifier(conn).await,
             "Object": "send-access",
         })
     }
@@ -176,8 +176,8 @@ use crate::api::EmptyResult;
 use crate::error::MapResult;
 
 impl Send {
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
         self.revision_date = Utc::now().naive_utc();
 
         db_run! { conn:
@@ -211,8 +211,8 @@ impl Send {
         }
     }
 
-    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
-        self.update_users_revision(conn);
+    pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
+        self.update_users_revision(conn).await;
 
         if self.atype == SendType::File as i32 {
             std::fs::remove_dir_all(std::path::Path::new(&crate::CONFIG.sends_folder()).join(&self.uuid)).ok();
@@ -226,17 +226,17 @@ impl Send {
     }
 
     /// Purge all sends that are past their deletion date.
-    pub fn purge(conn: &DbConn) {
-        for send in Self::find_by_past_deletion_date(conn) {
-            send.delete(conn).ok();
+    pub async fn purge(conn: &DbConn) {
+        for send in Self::find_by_past_deletion_date(conn).await {
+            send.delete(conn).await.ok();
         }
     }
 
-    pub fn update_users_revision(&self, conn: &DbConn) -> Vec<String> {
+    pub async fn update_users_revision(&self, conn: &DbConn) -> Vec<String> {
         let mut user_uuids = Vec::new();
         match &self.user_uuid {
             Some(user_uuid) => {
-                User::update_uuid_revision(user_uuid, conn);
+                User::update_uuid_revision(user_uuid, conn).await;
                 user_uuids.push(user_uuid.clone())
             }
             None => {
@@ -246,14 +246,14 @@ impl Send {
         user_uuids
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
-        for send in Self::find_by_user(user_uuid, conn) {
-            send.delete(conn)?;
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        for send in Self::find_by_user(user_uuid, conn).await {
+            send.delete(conn).await?;
         }
         Ok(())
     }
 
-    pub fn find_by_access_id(access_id: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_access_id(access_id: &str, conn: &DbConn) -> Option<Self> {
         use data_encoding::BASE64URL_NOPAD;
         use uuid::Uuid;
 
@@ -267,10 +267,10 @@ impl Send {
             Err(_) => return None,
         };
 
-        Self::find_by_uuid(&uuid, conn)
+        Self::find_by_uuid(&uuid, conn).await
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! {conn: {
             sends::table
                 .filter(sends::uuid.eq(uuid))
@@ -280,7 +280,7 @@ impl Send {
         }}
     }
 
-    pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             sends::table
                 .filter(sends::user_uuid.eq(user_uuid))
@@ -288,7 +288,7 @@ impl Send {
         }}
     }
 
-    pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             sends::table
                 .filter(sends::organization_uuid.eq(org_uuid))
@@ -296,7 +296,7 @@ impl Send {
         }}
     }
 
-    pub fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> {
         let now = Utc::now().naive_utc();
         db_run! {conn: {
             sends::table
diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs
index 6c874df1..03ca23b4 100644
--- a/src/db/models/two_factor.rs
+++ b/src/db/models/two_factor.rs
@@ -71,7 +71,7 @@ impl TwoFactor {
 
 /// Database methods
 impl TwoFactor {
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
         db_run! { conn:
             sqlite, mysql {
                 match diesel::replace_into(twofactor::table)
@@ -110,7 +110,7 @@ impl TwoFactor {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(twofactor::table.filter(twofactor::uuid.eq(self.uuid)))
                 .execute(conn)
@@ -118,7 +118,7 @@ impl TwoFactor {
         }}
     }
 
-    pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
         db_run! { conn: {
             twofactor::table
                 .filter(twofactor::user_uuid.eq(user_uuid))
@@ -129,7 +129,7 @@ impl TwoFactor {
         }}
     }
 
-    pub fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             twofactor::table
                 .filter(twofactor::user_uuid.eq(user_uuid))
@@ -140,7 +140,7 @@ impl TwoFactor {
         }}
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid)))
                 .execute(conn)
@@ -148,7 +148,7 @@ impl TwoFactor {
         }}
     }
 
-    pub fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult {
+    pub async fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult {
         let u2f_factors = db_run! { conn: {
             twofactor::table
                 .filter(twofactor::atype.eq(TwoFactorType::U2f as i32))
@@ -168,7 +168,7 @@ impl TwoFactor {
                 continue;
             }
 
-            let (_, mut webauthn_regs) = get_webauthn_registrations(&u2f.user_uuid, conn)?;
+            let (_, mut webauthn_regs) = get_webauthn_registrations(&u2f.user_uuid, conn).await?;
 
             // If the user already has webauthn registrations saved, don't overwrite them
             if !webauthn_regs.is_empty() {
@@ -207,10 +207,11 @@ impl TwoFactor {
             }
 
             u2f.data = serde_json::to_string(&regs)?;
-            u2f.save(conn)?;
+            u2f.save(conn).await?;
 
             TwoFactor::new(u2f.user_uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&webauthn_regs)?)
-                .save(conn)?;
+                .save(conn)
+                .await?;
         }
 
         Ok(())
diff --git a/src/db/models/two_factor_incomplete.rs b/src/db/models/two_factor_incomplete.rs
index d58398ec..1f292a08 100644
--- a/src/db/models/two_factor_incomplete.rs
+++ b/src/db/models/two_factor_incomplete.rs
@@ -22,7 +22,7 @@ db_object! {
 }
 
 impl TwoFactorIncomplete {
-    pub fn mark_incomplete(
+    pub async fn mark_incomplete(
         user_uuid: &str,
         device_uuid: &str,
         device_name: &str,
@@ -36,7 +36,7 @@ impl TwoFactorIncomplete {
         // Don't update the data for an existing user/device pair, since that
         // would allow an attacker to arbitrarily delay notifications by
         // sending repeated 2FA attempts to reset the timer.
-        let existing = Self::find_by_user_and_device(user_uuid, device_uuid, conn);
+        let existing = Self::find_by_user_and_device(user_uuid, device_uuid, conn).await;
         if existing.is_some() {
             return Ok(());
         }
@@ -55,15 +55,15 @@ impl TwoFactorIncomplete {
         }}
     }
 
-    pub fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult {
         if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() {
             return Ok(());
         }
 
-        Self::delete_by_user_and_device(user_uuid, device_uuid, conn)
+        Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await
     }
 
-    pub fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! { conn: {
             twofactor_incomplete::table
                 .filter(twofactor_incomplete::user_uuid.eq(user_uuid))
@@ -74,7 +74,7 @@ impl TwoFactorIncomplete {
         }}
     }
 
-    pub fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
+    pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             twofactor_incomplete::table
                 .filter(twofactor_incomplete::login_time.lt(dt))
@@ -84,11 +84,11 @@ impl TwoFactorIncomplete {
         }}
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn)
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await
     }
 
-    pub fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(twofactor_incomplete::table
                            .filter(twofactor_incomplete::user_uuid.eq(user_uuid))
@@ -98,7 +98,7 @@ impl TwoFactorIncomplete {
         }}
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+    pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid)))
                 .execute(conn)
diff --git a/src/db/models/user.rs b/src/db/models/user.rs
index 599661e5..80a0cb8e 100644
--- a/src/db/models/user.rs
+++ b/src/db/models/user.rs
@@ -192,12 +192,20 @@ use crate::db::DbConn;
 use crate::api::EmptyResult;
 use crate::error::MapResult;
 
+use futures::{stream, stream::StreamExt};
+
 /// Database methods
 impl User {
-    pub fn to_json(&self, conn: &DbConn) -> Value {
-        let orgs = UserOrganization::find_confirmed_by_user(&self.uuid, conn);
-        let orgs_json: Vec<Value> = orgs.iter().map(|c| c.to_json(conn)).collect();
-        let twofactor_enabled = !TwoFactor::find_by_user(&self.uuid, conn).is_empty();
+    pub async fn to_json(&self, conn: &DbConn) -> Value {
+        let orgs_json = stream::iter(UserOrganization::find_confirmed_by_user(&self.uuid, conn).await)
+            .then(|c| async {
+                let c = c; // Move out this single variable
+                c.to_json(conn).await
+            })
+            .collect::<Vec<Value>>()
+            .await;
+
+        let twofactor_enabled = !TwoFactor::find_by_user(&self.uuid, conn).await.is_empty();
 
         // TODO: Might want to save the status field in the DB
         let status = if self.password_hash.is_empty() {
@@ -227,7 +235,7 @@ impl User {
         })
     }
 
-    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
         if self.email.trim().is_empty() {
             err!("User email can't be empty")
         }
@@ -265,26 +273,26 @@ impl User {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
-        for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn) {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
+        for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await {
             if user_org.atype == UserOrgType::Owner {
                 let owner_type = UserOrgType::Owner as i32;
-                if UserOrganization::find_by_org_and_type(&user_org.org_uuid, owner_type, conn).len() <= 1 {
+                if UserOrganization::find_by_org_and_type(&user_org.org_uuid, owner_type, conn).await.len() <= 1 {
                     err!("Can't delete last owner")
                 }
             }
         }
 
-        Send::delete_all_by_user(&self.uuid, conn)?;
-        EmergencyAccess::delete_all_by_user(&self.uuid, conn)?;
-        UserOrganization::delete_all_by_user(&self.uuid, conn)?;
-        Cipher::delete_all_by_user(&self.uuid, conn)?;
-        Favorite::delete_all_by_user(&self.uuid, conn)?;
-        Folder::delete_all_by_user(&self.uuid, conn)?;
-        Device::delete_all_by_user(&self.uuid, conn)?;
-        TwoFactor::delete_all_by_user(&self.uuid, conn)?;
-        TwoFactorIncomplete::delete_all_by_user(&self.uuid, conn)?;
-        Invitation::take(&self.email, conn); // Delete invitation if any
+        Send::delete_all_by_user(&self.uuid, conn).await?;
+        EmergencyAccess::delete_all_by_user(&self.uuid, conn).await?;
+        UserOrganization::delete_all_by_user(&self.uuid, conn).await?;
+        Cipher::delete_all_by_user(&self.uuid, conn).await?;
+        Favorite::delete_all_by_user(&self.uuid, conn).await?;
+        Folder::delete_all_by_user(&self.uuid, conn).await?;
+        Device::delete_all_by_user(&self.uuid, conn).await?;
+        TwoFactor::delete_all_by_user(&self.uuid, conn).await?;
+        TwoFactorIncomplete::delete_all_by_user(&self.uuid, conn).await?;
+        Invitation::take(&self.email, conn).await; // Delete invitation if any
 
         db_run! {conn: {
             diesel::delete(users::table.filter(users::uuid.eq(self.uuid)))
@@ -293,13 +301,13 @@ impl User {
         }}
     }
 
-    pub fn update_uuid_revision(uuid: &str, conn: &DbConn) {
-        if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn) {
+    pub async fn update_uuid_revision(uuid: &str, conn: &DbConn) {
+        if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await {
             warn!("Failed to update revision for {}: {:#?}", uuid, e);
         }
     }
 
-    pub fn update_all_revisions(conn: &DbConn) -> EmptyResult {
+    pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult {
         let updated_at = Utc::now().naive_utc();
 
         db_run! {conn: {
@@ -312,13 +320,13 @@ impl User {
         }}
     }
 
-    pub fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {
+    pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {
         self.updated_at = Utc::now().naive_utc();
 
-        Self::_update_revision(&self.uuid, &self.updated_at, conn)
+        Self::_update_revision(&self.uuid, &self.updated_at, conn).await
     }
 
-    fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {
+    async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {
         db_run! {conn: {
             crate::util::retry(|| {
                 diesel::update(users::table.filter(users::uuid.eq(uuid)))
@@ -329,7 +337,7 @@ impl User {
         }}
     }
 
-    pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
         let lower_mail = mail.to_lowercase();
         db_run! {conn: {
             users::table
@@ -340,20 +348,20 @@ impl User {
         }}
     }
 
-    pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
         db_run! {conn: {
             users::table.filter(users::uuid.eq(uuid)).first::<UserDb>(conn).ok().from_db()
         }}
     }
 
-    pub fn get_all(conn: &DbConn) -> Vec<Self> {
+    pub async fn get_all(conn: &DbConn) -> Vec<Self> {
         db_run! {conn: {
             users::table.load::<UserDb>(conn).expect("Error loading users").from_db()
         }}
     }
 
-    pub fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> {
-        match Device::find_latest_active_by_user(&self.uuid, conn) {
+    pub async fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> {
+        match Device::find_latest_active_by_user(&self.uuid, conn).await {
             Some(device) => Some(device.updated_at),
             None => None,
         }
@@ -368,7 +376,7 @@ impl Invitation {
         }
     }
 
-    pub fn save(&self, conn: &DbConn) -> EmptyResult {
+    pub async fn save(&self, conn: &DbConn) -> EmptyResult {
         if self.email.trim().is_empty() {
             err!("Invitation email can't be empty")
         }
@@ -393,7 +401,7 @@ impl Invitation {
         }
     }
 
-    pub fn delete(self, conn: &DbConn) -> EmptyResult {
+    pub async fn delete(self, conn: &DbConn) -> EmptyResult {
         db_run! {conn: {
             diesel::delete(invitations::table.filter(invitations::email.eq(self.email)))
                 .execute(conn)
@@ -401,7 +409,7 @@ impl Invitation {
         }}
     }
 
-    pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
+    pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
         let lower_mail = mail.to_lowercase();
         db_run! {conn: {
             invitations::table
@@ -412,9 +420,9 @@ impl Invitation {
         }}
     }
 
-    pub fn take(mail: &str, conn: &DbConn) -> bool {
-        match Self::find_by_mail(mail, conn) {
-            Some(invitation) => invitation.delete(conn).is_ok(),
+    pub async fn take(mail: &str, conn: &DbConn) -> bool {
+        match Self::find_by_mail(mail, conn).await {
+            Some(invitation) => invitation.delete(conn).await.is_ok(),
             None => false,
         }
     }
diff --git a/src/main.rs b/src/main.rs
index bcbf1d29..17305a5e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,6 @@
 #![forbid(unsafe_code)]
+// #![warn(rust_2018_idioms)]
+#![warn(rust_2021_compatibility)]
 #![cfg_attr(feature = "unstable", feature(ip))]
 // The recursion_limit is mainly triggered by the json!() macro.
 // The more key/value pairs there are the more recursion occurs.
@@ -72,7 +74,7 @@ async fn main() -> Result<(), Error> {
 
     let pool = create_db_pool();
     schedule_jobs(pool.clone()).await;
-    crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&pool.get().await.unwrap()).unwrap();
+    crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&pool.get().await.unwrap()).await.unwrap();
 
     launch_rocket(pool, extra_debug).await // Blocks until program termination.
 }