From 65240b2b1d30aa0cd3675b9787d154c02ea7818d Mon Sep 17 00:00:00 2001 From: Rasmus Kjellberg <2277443+kjellberg@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:12:05 +0200 Subject: [PATCH] feat: pages for disabling two factor authentication --- .../users/two_factor_controller.rb | 16 +++++++++++++ app/models/user.rb | 3 +-- app/views/users/two_factor/disable.html.erb | 23 ++++++++++++++++++ app/views/users/two_factor/show.html.erb | 2 +- config/locales/kiqr.en.yml | 9 +++++++ config/routes/authentication.rb | 2 ++ .../users/two_factor_controller_test.rb | 24 +++++++++++++++++++ test/system/users/two_factor_test.rb | 2 +- 8 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 app/views/users/two_factor/disable.html.erb diff --git a/app/controllers/users/two_factor_controller.rb b/app/controllers/users/two_factor_controller.rb index e7f8dc9..2d44ca5 100644 --- a/app/controllers/users/two_factor_controller.rb +++ b/app/controllers/users/two_factor_controller.rb @@ -32,6 +32,22 @@ def verify end end + def disable + redirect_to edit_two_factor_path unless enabled? + end + + def destroy + return redirect_to edit_two_factor_path unless enabled? + + if @user.validate_and_consume_otp!(params.dig(:user, :otp_attempt)) + @user.update(otp_required_for_login: false, otp_backup_codes: []) + redirect_to edit_two_factor_path, notice: I18n.t("users.two_factor.disable.disabled") + else + @user.errors.add(:otp_attempt, :invalid) + render :disable, status: :unprocessable_entity + end + end + private # Don't refresh the OTP secret if it's already enabled. This may lock the user diff --git a/app/models/user.rb b/app/models/user.rb index 6b47082..c425e40 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -19,8 +19,7 @@ def onboarded? def otp_uri issuer = Kiqr::Config.app_name - label = "#{issuer}:#{email}" - otp_provisioning_uri(label, issuer: issuer) + otp_provisioning_uri(email, issuer: issuer) end def reset_otp_secret! diff --git a/app/views/users/two_factor/disable.html.erb b/app/views/users/two_factor/disable.html.erb new file mode 100644 index 0000000..b3947d1 --- /dev/null +++ b/app/views/users/two_factor/disable.html.erb @@ -0,0 +1,23 @@ +<% title t(".title") %> + +<%= render(PageLayouts::Settings::Component.new( + title: t(".title"), + description: t(".description") +)) do %> +
+
+

+ <%= t(".title") %> +

+
+
+

<%= t(".instructions") %>

+ <%= simple_form_for(@user, url: destroy_two_factor_path, method: :delete) do |f| %> + <%= f.input :otp_attempt, label: t(".label"), placeholder: t(".placeholder") %> +
+ <%= f.submit t(".button"), class: "button danger" %> +
+ <% end %> +
+
+<% end %> diff --git a/app/views/users/two_factor/show.html.erb b/app/views/users/two_factor/show.html.erb index 1cd1afd..724465b 100644 --- a/app/views/users/two_factor/show.html.erb +++ b/app/views/users/two_factor/show.html.erb @@ -13,7 +13,7 @@

<%= t(".content") %>

<%= link_to t(".enable.button"), new_two_factor_path, class:"button" unless enabled? %> - <%= link_to t(".disable.button"), "#", class:"button danger" if enabled? %> + <%= link_to t(".disable.button"), disable_two_factor_path, class:"button danger" if enabled? %>
<% end %> diff --git a/config/locales/kiqr.en.yml b/config/locales/kiqr.en.yml index ade66a0..c03c77a 100644 --- a/config/locales/kiqr.en.yml +++ b/config/locales/kiqr.en.yml @@ -123,6 +123,15 @@ en: - "Enter the 6-digit code generated by your authenticator app." success: "Two-factor authentication has been enabled." invalid_code: "Invalid code. Please try again." + disable: + title: "Disable two-factor authentication" + description: "Remove the extra layer of security from your user account." + instructions: "To disable two-factor authentication, enter the 6-digit code from your authenticator app." + disabled: "Two-factor authentication has been disabled." + invalid_code: "Invalid code. Please try again." + label: "One-time password" + placeholder: "Enter the 6-digit code" + button: "Disable two-factor authentication" form: instructions: "Enter the code provided by your app to finish the two factor setup:" label: "One-time password" diff --git a/config/routes/authentication.rb b/config/routes/authentication.rb index 8b58796..9a5d3b7 100644 --- a/config/routes/authentication.rb +++ b/config/routes/authentication.rb @@ -12,7 +12,9 @@ get "two-factor" => "two_factor#show", :as => :edit_two_factor get "two-factor/new" => "two_factor#new", :as => :new_two_factor get "two-factor/setup" => "two_factor#setup", :as => :setup_two_factor + get "two-factor/disable" => "two_factor#disable", :as => :disable_two_factor post "two-factor/verify" => "two_factor#verify", :as => :verify_two_factor + delete "two-factor/destroy" => "two_factor#destroy", :as => :destroy_two_factor get "delete" => "cancellations#show", :as => :delete_user_registration end diff --git a/test/controllers/users/two_factor_controller_test.rb b/test/controllers/users/two_factor_controller_test.rb index 9c5b0de..61fe2a5 100644 --- a/test/controllers/users/two_factor_controller_test.rb +++ b/test/controllers/users/two_factor_controller_test.rb @@ -18,4 +18,28 @@ class Users::TwoFactorControllerTest < ActionDispatch::IntegrationTest get new_two_factor_path assert_not_equal user.otp_secret, user.reload.otp_secret end + + test "redirects to show page if two factor is alredy disabled" do + user = create(:user) + sign_in user + get disable_two_factor_path + assert_redirected_to edit_two_factor_path + + delete destroy_two_factor_path + assert_redirected_to edit_two_factor_path + end + + test "requires valid otp code to disable two factor authentication" do + user = create(:user, :otp_enabled) + sign_in user + + delete destroy_two_factor_path, params: {user: {otp_attempt: "123456"}} + assert_response :unprocessable_entity + assert_template "two_factor/disable" + assert user.reload.otp_required_for_login? + + delete destroy_two_factor_path, params: {user: {otp_attempt: user.current_otp}} + assert_redirected_to edit_two_factor_path + assert_not user.reload.otp_required_for_login? + end end diff --git a/test/system/users/two_factor_test.rb b/test/system/users/two_factor_test.rb index 2c32168..b698d05 100644 --- a/test/system/users/two_factor_test.rb +++ b/test/system/users/two_factor_test.rb @@ -52,7 +52,7 @@ def prepare_otp_setup(user) assert_not_equal first_image, second_image end - test "Shows instructions on how to disable two factor authentication" do + test "shows instructions on how to disable two factor authentication" do user = create(:user, :otp_enabled) sign_in user visit edit_two_factor_path