about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-01-27 20:28:46 +0100
committerEugen Rochko <eugen@zeonfederated.com>2017-01-27 20:35:16 +0100
commitba192f12e381842c90df0fab2fcb1a23cae97fc4 (patch)
tree1af8032d690d9c20af8b481d42978829b01334b6 /app
parent237cb41ab4d841fb215ce6707c8d7695ef44b103 (diff)
Added optional two-factor authentication
Diffstat (limited to 'app')
-rw-r--r--app/assets/stylesheets/forms.scss32
-rw-r--r--app/controllers/auth/sessions_controller.rb6
-rw-r--r--app/controllers/settings/two_factor_auths_controller.rb28
-rw-r--r--app/models/user.rb4
-rw-r--r--app/views/auth/sessions/new.html.haml1
-rw-r--r--app/views/settings/shared/_links.html.haml4
-rw-r--r--app/views/settings/two_factor_auths/show.html.haml17
7 files changed, 88 insertions, 4 deletions
diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss
index 365396511..560388f8f 100644
--- a/app/assets/stylesheets/forms.scss
+++ b/app/assets/stylesheets/forms.scss
@@ -7,6 +7,18 @@ code {
   max-width: 400px;
   padding: 20px;
   margin: 0 auto;
+
+  p {
+    font-size: 14px;
+    line-height: 18px;
+    color: $color2;
+    margin-bottom: 20px;
+
+    strong {
+      color: $color5;
+      font-weight: 500;
+    }
+  }
 }
 
 .simple_form {
@@ -118,7 +130,7 @@ code {
     margin-top: 30px;
   }
 
-  button {
+  button, .block-button {
     display: block;
     width: 100%;
     border: 0;
@@ -128,6 +140,9 @@ code {
     font-size: 18px;
     padding: 10px;
     text-transform: uppercase;
+    text-decoration: none;
+    text-align: center;
+    box-sizing: border-box;
     cursor: pointer;
     font-weight: 500;
     outline: 0;
@@ -176,7 +191,7 @@ code {
   text-align: center;
 
   a {
-    color: white;
+    color: $color5;
     text-decoration: none;
 
     &:hover {
@@ -200,3 +215,16 @@ code {
     font-weight: 500;
   }
 }
+
+.qr-code {
+  background: #fff;
+  padding: 4px;
+  margin-bottom: 20px;
+  box-shadow: 0 0 15px rgba($color8, 0.2);
+  display: inline-block;
+
+  svg {
+    display: block;
+    margin: 0;
+  }
+}
diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb
index c8350f9a1..889b20e11 100644
--- a/app/controllers/auth/sessions_controller.rb
+++ b/app/controllers/auth/sessions_controller.rb
@@ -5,6 +5,8 @@ class Auth::SessionsController < Devise::SessionsController
 
   layout 'auth'
 
+  before_action :configure_sign_in_params, only: [:create]
+
   def create
     super do |resource|
       remember_me(resource)
@@ -13,6 +15,10 @@ class Auth::SessionsController < Devise::SessionsController
 
   protected
 
+  def configure_sign_in_params
+    devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
+  end
+
   def after_sign_in_path_for(_resource)
     last_url = stored_location_for(:user)
 
diff --git a/app/controllers/settings/two_factor_auths_controller.rb b/app/controllers/settings/two_factor_auths_controller.rb
new file mode 100644
index 000000000..66a82aab7
--- /dev/null
+++ b/app/controllers/settings/two_factor_auths_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class Settings::TwoFactorAuthsController < ApplicationController
+  layout 'auth'
+
+  before_action :authenticate_user!
+
+  def show
+    return unless current_user.otp_required_for_login
+
+    @qrcode = RQRCode::QRCode.new(current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain))
+  end
+
+  def enable
+    current_user.otp_required_for_login = true
+    current_user.otp_secret = User.generate_otp_secret
+    current_user.save!
+
+    redirect_to settings_two_factor_auth_path
+  end
+
+  def disable
+    current_user.otp_required_for_login = false
+    current_user.save!
+
+    redirect_to settings_two_factor_auth_path
+  end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 71d3ee0b8..b34144f2c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -3,7 +3,9 @@
 class User < ApplicationRecord
   include Settings::Extend
 
-  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
+  devise :registerable, :recoverable,
+         :rememberable, :trackable, :validatable, :confirmable,
+         :two_factor_authenticatable, otp_secret_encryption_key: ENV['OTP_SECRET']
 
   belongs_to :account, inverse_of: :user
   accepts_nested_attributes_for :account
diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml
index 93b9629f1..192a54bc6 100644
--- a/app/views/auth/sessions/new.html.haml
+++ b/app/views/auth/sessions/new.html.haml
@@ -4,6 +4,7 @@
 = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
   = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
   = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
+  = f.input :otp_attempt, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt') }
 
   .actions
     = f.button :button, t('auth.login'), type: :submit
diff --git a/app/views/settings/shared/_links.html.haml b/app/views/settings/shared/_links.html.haml
index a6e90f457..6490ffdd8 100644
--- a/app/views/settings/shared/_links.html.haml
+++ b/app/views/settings/shared/_links.html.haml
@@ -5,4 +5,6 @@
     %li= link_to t('settings.preferences'), settings_preferences_path
   - if controller_name != 'registrations'
     %li= link_to t('auth.change_password'), edit_user_registration_path
-  %li= link_to t('settings.back'), root_path
\ No newline at end of file
+  - if controller_name != 'two_factor_auths'
+    %li= link_to t('settings.two_factor_auth'), settings_two_factor_auth_path
+  %li= link_to t('settings.back'), root_path
diff --git a/app/views/settings/two_factor_auths/show.html.haml b/app/views/settings/two_factor_auths/show.html.haml
new file mode 100644
index 000000000..5070bb9d4
--- /dev/null
+++ b/app/views/settings/two_factor_auths/show.html.haml
@@ -0,0 +1,17 @@
+- content_for :page_title do
+  = t('settings.two_factor_auth')
+
+- if current_user.otp_required_for_login
+  %p= t('two_factor_auth.instructions_html')
+
+  .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 5)
+
+  .simple_form
+    = link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
+- else
+  %p= t('two_factor_auth.description_html')
+
+  .simple_form
+    = link_to t('two_factor_auth.enable'), enable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
+
+.form-footer= render "settings/shared/links"