diff --git a/Gemfile b/Gemfile
index b897dc0a7412c690382115c7cf527c652941e4a5..98a594be1c7613e46c8e18d48fb32b72948e1d6c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,9 +18,8 @@ gem "mysql2", '~> 0.3.16', group: :mysql
 gem "pg", '~> 0.18.2', group: :postgres
 
 # Authentication libraries
-gem 'devise',                 '~> 3.5.4'
+gem 'devise',                 '~> 4.0'
 gem 'doorkeeper',             '~> 3.1'
-gem 'devise-async',           '~> 0.9.0'
 gem 'omniauth',               '~> 1.3.1'
 gem 'omniauth-auth0',         '~> 1.4.1'
 gem 'omniauth-azure-oauth2',  '~> 0.0.6'
@@ -43,7 +42,7 @@ gem 'recaptcha', require: 'recaptcha/rails'
 gem 'akismet', '~> 2.0'
 
 # Two-factor authentication
-gem 'devise-two-factor', '~> 2.0.0'
+gem 'devise-two-factor', '~> 3.0.0'
 gem 'rqrcode-rails3', '~> 0.1.7'
 gem 'attr_encrypted', '~> 1.3.4'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index fa2b72b2524f06e8ae2d437f2edbbea69b46b29a..30a0a2fd183158d9d1603157e04ab2b065c4fe91 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -73,7 +73,7 @@ GEM
       thread_safe (~> 0.3, >= 0.3.1)
     babosa (1.0.2)
     base32 (0.3.2)
-    bcrypt (3.1.10)
+    bcrypt (3.1.11)
     benchmark-ips (2.3.0)
     better_errors (1.0.1)
       coderay (>= 1.0.0)
@@ -155,21 +155,18 @@ GEM
       activerecord (>= 3.2.0, < 5.0)
     descendants_tracker (0.0.4)
       thread_safe (~> 0.3, >= 0.3.1)
-    devise (3.5.4)
+    devise (4.1.1)
       bcrypt (~> 3.0)
       orm_adapter (~> 0.1)
-      railties (>= 3.2.6, < 5)
+      railties (>= 4.1.0, < 5.1)
       responders
-      thread_safe (~> 0.1)
       warden (~> 1.2.3)
-    devise-async (0.9.0)
-      devise (~> 3.2)
-    devise-two-factor (2.0.1)
+    devise-two-factor (3.0.0)
       activesupport
-      attr_encrypted (~> 1.3.2)
-      devise (~> 3.5.0)
+      attr_encrypted (>= 1.3, < 4, != 2)
+      devise (~> 4.0)
       railties
-      rotp (~> 2)
+      rotp (~> 2.0)
     diff-lcs (1.2.5)
     diffy (3.0.7)
     docile (1.1.5)
@@ -656,7 +653,7 @@ GEM
     responders (2.1.1)
       railties (>= 4.2.0, < 5.1)
     rinku (1.7.3)
-    rotp (2.1.1)
+    rotp (2.1.2)
     rouge (1.10.1)
     rqrcode (0.7.0)
       chunky_png
@@ -859,7 +856,7 @@ GEM
       coercible (~> 1.0)
       descendants_tracker (~> 0.0, >= 0.0.3)
       equalizer (~> 0.0, >= 0.0.9)
-    warden (1.2.4)
+    warden (1.2.6)
       rack (>= 1.0)
     web-console (2.3.0)
       activemodel (>= 4.0)
@@ -919,9 +916,8 @@ DEPENDENCIES
   d3_rails (~> 3.5.0)
   database_cleaner (~> 1.4.0)
   default_value_for (~> 3.0.0)
-  devise (~> 3.5.4)
-  devise-async (~> 0.9.0)
-  devise-two-factor (~> 2.0.0)
+  devise (~> 4.0)
+  devise-two-factor (~> 3.0.0)
   diffy (~> 3.0.3)
   doorkeeper (~> 3.1)
   dropzonejs-rails (~> 0.7.1)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1429ee40bb72eb48e41b2fe5f3d7f56c801f8c28..144febcea12c90eaf5b8ab66e5c6c09b26df4880 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -232,7 +232,7 @@ def view_to_html_string(partial, locals = {})
   end
 
   def configure_permitted_parameters
-    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me, :otp_attempt) }
+    devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
   end
 
   def hexdigest(string)
diff --git a/app/models/user.rb b/app/models/user.rb
index 6a09b78455b7ae83a6be7300c36f86df2a2d3270..b5f478b3865d7dc7b637a4afcd3280fd3980beac 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -27,7 +27,7 @@ class User < ActiveRecord::Base
   devise :two_factor_backupable, otp_number_of_backup_codes: 10
   serialize :otp_backup_codes, JSON
 
-  devise :lockable, :async, :recoverable, :rememberable, :trackable,
+  devise :lockable, :recoverable, :rememberable, :trackable,
     :validatable, :omniauthable, :confirmable, :registerable
 
   attr_accessor :force_random_password
diff --git a/config/initializers/devise_async.rb b/config/initializers/devise_async.rb
deleted file mode 100644
index 05a1852cdbd9d141a757755983adc59ad467f3ca..0000000000000000000000000000000000000000
--- a/config/initializers/devise_async.rb
+++ /dev/null
@@ -1 +0,0 @@
-Devise::Async.backend = :sidekiq