diff --git a/Gemfile b/Gemfile
index f950c5be15463b7a0c0001471c3196ff380d8e4d..65a641c602c13b574d5179ac316d21f4fc3bdac0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -34,6 +34,10 @@ gem 'omniauth-bitbucket'
 gem 'doorkeeper', '2.1.3'
 gem "rack-oauth2", "~> 1.0.5"
 
+# Two-factor authentication
+gem 'devise-two-factor'
+gem 'rqrcode-rails3'
+
 # Browser detection
 gem "browser"
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 6f58c4f4fda1d3283e6bda4d5b2cc34b3cdbae31..35faa9895ba886af39d0da4cd8b61f8a3c712d96 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -46,6 +46,8 @@ GEM
     ast (2.0.0)
     astrolabe (1.3.0)
       parser (>= 2.2.0.pre.3, < 3.0)
+    attr_encrypted (1.3.3)
+      encryptor (>= 1.3.0)
     attr_required (1.0.0)
     autoprefixer-rails (5.1.11)
       execjs
@@ -136,6 +138,13 @@ GEM
       warden (~> 1.2.3)
     devise-async (0.9.0)
       devise (~> 3.2)
+    devise-two-factor (1.0.1)
+      activemodel
+      activesupport
+      attr_encrypted (~> 1.3.2)
+      devise (~> 3.2.4)
+      rails
+      rotp (~> 1.6.1)
     diff-lcs (1.2.5)
     diffy (3.0.3)
     docile (1.1.5)
@@ -147,6 +156,7 @@ GEM
     email_spec (1.5.0)
       launchy (~> 2.1)
       mail (~> 2.2)
+    encryptor (1.3.0)
     enumerize (0.7.0)
       activesupport (>= 3.2)
     equalizer (0.0.8)
@@ -482,7 +492,11 @@ GEM
     rest-client (1.6.7)
       mime-types (>= 1.16)
     rinku (1.7.3)
+    rotp (1.6.1)
     rouge (1.7.7)
+    rqrcode (0.4.2)
+    rqrcode-rails3 (0.1.7)
+      rqrcode (>= 0.4.2)
     rspec (2.99.0)
       rspec-core (~> 2.99.0)
       rspec-expectations (~> 2.99.0)
@@ -691,6 +705,7 @@ DEPENDENCIES
   default_value_for (~> 3.0.0)
   devise (= 3.2.4)
   devise-async (= 0.9.0)
+  devise-two-factor
   diffy (~> 3.0.3)
   doorkeeper (= 2.1.3)
   dropzonejs-rails
@@ -762,6 +777,7 @@ DEPENDENCIES
   redis-rails
   request_store
   rerun (~> 0.10.0)
+  rqrcode-rails3
   rspec-rails (= 2.99)
   rubocop (= 0.28.0)
   rugments