<% else %>
From f98be92dcef87f10200f65e3d0b2f262304b1153 Mon Sep 17 00:00:00 2001
From: EchoEkhi
Date: Mon, 18 May 2026 06:50:46 +0100
Subject: [PATCH 06/10] rubocop
---
app/views/users/sessions/totp.html.erb | 2 +-
features/users/authenticate_users_with_totp.feature | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/views/users/sessions/totp.html.erb b/app/views/users/sessions/totp.html.erb
index b20334bdfa4..50e86734ee4 100644
--- a/app/views/users/sessions/totp.html.erb
+++ b/app/views/users/sessions/totp.html.erb
@@ -14,4 +14,4 @@
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/features/users/authenticate_users_with_totp.feature b/features/users/authenticate_users_with_totp.feature
index 1b6fc4975ff..dc634d22414 100644
--- a/features/users/authenticate_users_with_totp.feature
+++ b/features/users/authenticate_users_with_totp.feature
@@ -1,4 +1,4 @@
-@admin
+@users
Feature: Authenticate Users With TOTP 2FA
Scenario: Users can enable TOTP 2FA
Given the following activated users exist
@@ -65,7 +65,7 @@ Feature: Authenticate Users With TOTP 2FA
And I press "Disable"
Then I should see "Successfully disabled two-step verification."
- Scenario: Admins cannot disable TOTP 2FA if they provide a wrong password
+ Scenario: Users cannot disable TOTP 2FA if they provide a wrong password
Given the following activated users exist
| login | password |
| user | testpassword |
@@ -110,7 +110,7 @@ Feature: Authenticate Users With TOTP 2FA
| login | password |
| user | testpassword |
And user "user" has TOTP 2FA enabled
- When I go to the admin login page
+ When I go to the login page
And I fill in "Username or email" with "user"
And I fill in "Password" with "testpassword"
And I press "Log In"
@@ -133,7 +133,7 @@ Feature: Authenticate Users With TOTP 2FA
And I press "Log In" within "div#main"
Then I should see "Successfully logged in"
When I log out
- And I go to the admin login page
+ And I go to the login page
And I fill in "Username or email" with "user"
And I fill in "Password" with "testpassword"
And I press "Log In"
From 3041d94aaeb106b4b445c2be20518c61c521fb4c Mon Sep 17 00:00:00 2001
From: EchoEkhi
Date: Tue, 26 May 2026 07:59:58 +0100
Subject: [PATCH 07/10] Change GET to POST for backup code generation to
prevent reflection attack
---
app/controllers/users/totp_controller.rb | 19 ++++++++++++++++---
app/views/preferences/index.html.erb | 2 +-
.../confirm_regenerate_backup_codes.html.erb | 13 +++++++++++++
...tml.erb => generate_backup_codes.html.erb} | 0
...ate.erb => reauthenticate_create.html.erb} | 5 ++++-
config/locales/controllers/en.yml | 10 +++++++---
config/locales/views/en.yml | 17 ++++++++++-------
config/routes.rb | 3 ++-
.../authenticate_users_with_totp.feature | 2 ++
.../controllers/users/totp_controller_spec.rb | 14 +++++++-------
10 files changed, 62 insertions(+), 23 deletions(-)
create mode 100644 app/views/users/totp/confirm_regenerate_backup_codes.html.erb
rename app/views/users/totp/{show_backup_codes.html.erb => generate_backup_codes.html.erb} (100%)
rename app/views/users/totp/{reauthenticate_create.erb => reauthenticate_create.html.erb} (91%)
diff --git a/app/controllers/users/totp_controller.rb b/app/controllers/users/totp_controller.rb
index 04b9c0ed14d..9b4d4141ab6 100644
--- a/app/controllers/users/totp_controller.rb
+++ b/app/controllers/users/totp_controller.rb
@@ -27,20 +27,33 @@ def create
if current_user.validate_and_consume_otp!(params[:totp_attempt])
current_user.enable_totp!
- flash[:notice] = t(".success")
- redirect_to show_backup_codes_user_totp_path
+ flash.now[:notice] = t(".success")
+
+ @page_subtitle = t(".page_title")
+ @backup_codes = current_user.generate_otp_backup_codes!
+ current_user.save!
+
+ render action: :generate_backup_codes and return
else
flash.now[:error] = t(".incorrect_code")
render action: :new and return
end
end
- def show_backup_codes
+ # GET /users//totp/confirm_regenerate_backup_codes
+ def confirm_regenerate_backup_codes
+ @page_subtitle = t(".page_title")
+ end
+
+ # POST /users//totp/generate_backup_codes
+ def generate_backup_codes
unless current_user.totp_enabled?
flash[:error] = t(".not_enabled")
redirect_to new_user_totp_path and return
end
+ flash.now[:notice] = t(".success")
+
@page_subtitle = t(".page_title")
@backup_codes = current_user.generate_otp_backup_codes!
current_user.save!
diff --git a/app/views/preferences/index.html.erb b/app/views/preferences/index.html.erb
index 16a8ccfd569..3e1bfaa0d74 100644
--- a/app/views/preferences/index.html.erb
+++ b/app/views/preferences/index.html.erb
@@ -32,7 +32,7 @@
<%= setup_key = current_user.otp_secret.scan(/.{1,4}/).join(" ") %>
diff --git a/config/locales/controllers/en.yml b/config/locales/controllers/en.yml
index bc32510ad58..76e9e22d9de 100644
--- a/config/locales/controllers/en.yml
+++ b/config/locales/controllers/en.yml
@@ -396,21 +396,25 @@ en:
already_enabled: TOTP two-step verification is already enabled.
confirm_disable:
page_title: Disable Two-Step Verification
+ confirm_regenerate_backup_codes:
+ page_title: Two-Step Verification Backup Codes
create:
incorrect_code: Incorrect verification code. Your code may have expired, or you may need to set up your authenticator app again.
+ page_title: Two-Step Verification Backup Codes
success: Successfully enabled two-step verification; please make note of your backup codes.
disable:
failure: Could not disable two-step verification.
incorrect_password: Your password was incorrect. Please try again or, if you've forgotten your password, log out and reset your password via the link on the login form.
success: Successfully disabled two-step verification.
+ generate_backup_codes:
+ not_enabled: Please enable two-step verification first.
+ page_title: Two-Step Verification Backup Codes
+ success: Successfully re-generated your backup codes; please make note of your new backup codes, your old backup codes will no longer work.
new:
page_title: Confirm Password
reauthenticate_create:
incorrect_password: Your password was incorrect. Please try again or, if you've forgotten your password, log out and reset your password via the link on the login form.
page_title: Enable Two-Step Verification
- show_backup_codes:
- not_enabled: Please enable two-step verification first.
- page_title: Two-Step Verification Backup Codes
works:
create:
draft_notice_html: Draft was successfully created. It will be %{scheduled_for_deletion_bold} on %{deletion_date}.
diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml
index 0a75a24a4c4..98cf1f772b7 100644
--- a/config/locales/views/en.yml
+++ b/config/locales/views/en.yml
@@ -3250,6 +3250,14 @@ en:
password_label: Password
required_before_disabling: Before disabling two-step verification, you need to verify your password.
submit: Disable Two-Step Verification
+ confirm_regenerate_backup_codes:
+ confirm_html: Are you sure you want to re-generate your two-step verification backup codes? Your existing backup codes will no longer work.
+ generate_backup_codes:
+ backup_codes_copied: Backup Codes Copied!
+ copy_to_clipboard: Copy Backup Codes to Clipboard
+ finish: Finish
+ page_heading: Two-Step Verification Backup Codes
+ write_codes_down: 'Keep these backup codes in a safe and secure place in case you lose access to your authenticator app:'
new:
page_heading: Confirm Password
password_label: Password
@@ -3262,12 +3270,13 @@ en:
heading: About Two-Step Verification
app_setup:
heading: 'Step 1: Set up your authenticator app'
- instructions: Use your authenticator app to scan the QR code or enter the manual setup key. If you are using the mobile device the app is already installed on, click the import settings button. The app will give you a 6-digit code, which you'll use in the next step.
+ instructions: Use your authenticator app to scan the QR code or enter the manual setup key. If you are using the same device the app is installed on, click the import settings button. The app will give you a 6-digit code, which you'll use in the next step.
key:
copied: Copied!
copy_to_clipboard: Copy
format_note: Spaces and capitalization don't matter
heading: Setup Key
+ qr_scan_note: Scan with your authenticator app
enter_code:
heading: 'Step 2: Enter your 6-digit code'
instructions: Enter the 6-digit code from your app to enable two-step verification.
@@ -3275,12 +3284,6 @@ en:
please_wait: Please wait...
submit: Enable Two-Step Verification
page_heading: Set Up Two-Step Verification
- show_backup_codes:
- backup_codes_copied: Backup Codes Copied!
- copy_to_clipboard: Copy Backup Codes to Clipboard
- finish: Finish
- page_heading: Two-Step Verification Backup Codes
- write_codes_down: 'Keep these backup codes in a safe and secure place in case you lose access to your authenticator app:'
works:
adult:
caution: This work could have adult content. If you continue, you have agreed that you are willing to see such content.
diff --git a/config/routes.rb b/config/routes.rb
index 73cf43a3362..4c8d53e7e58 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -311,7 +311,8 @@
resources :nominations, controller: "tag_set_nominations", only: [:index]
resources :preferences, only: [:index, :update]
resource :totp, controller: "users/totp", only: [:create, :new] do
- get :show_backup_codes
+ get :confirm_regenerate_backup_codes
+ post :generate_backup_codes
get :confirm_disable
post :disable
post :reauthenticate_create
diff --git a/features/users/authenticate_users_with_totp.feature b/features/users/authenticate_users_with_totp.feature
index dc634d22414..03e333aba4f 100644
--- a/features/users/authenticate_users_with_totp.feature
+++ b/features/users/authenticate_users_with_totp.feature
@@ -87,6 +87,8 @@ Feature: Authenticate Users With TOTP 2FA
And user "user" has TOTP 2FA enabled
When I follow "My Preferences"
And I follow "Re-generate two-step verification backup codes"
+ Then I should see "Are you sure you want to re-generate your two-step verification backup codes?"
+ When I press "Yes, Regenerate Codes"
Then I should see "Two-Step Verification Backup Codes"
When I follow "Finish"
Then I should see "Set My Preferences"
diff --git a/spec/controllers/users/totp_controller_spec.rb b/spec/controllers/users/totp_controller_spec.rb
index c629d83b870..42b321a7161 100644
--- a/spec/controllers/users/totp_controller_spec.rb
+++ b/spec/controllers/users/totp_controller_spec.rb
@@ -112,7 +112,7 @@
fake_login_known_user(user)
post :create, params: { user_id: user.login, totp_attempt: user.current_otp }
expect(user.reload.totp_enabled?).to be_truthy
- it_redirects_to_with_notice(show_backup_codes_user_totp_path, "Successfully enabled two-step verification; please make note of your backup codes.")
+ expect(flash[:notice]).to eq("Successfully enabled two-step verification; please make note of your backup codes.")
end
it "denies access when TOTP code is wrong" do
@@ -136,18 +136,18 @@
end
end
- describe "GET #show_backup_codes" do
+ describe "POST #generate_backup_codes" do
let(:user) { create(:user, otp_required_for_login: true) }
let(:other_user) { create(:user) }
it "denies access to guest users" do
- get :show_backup_codes, params: { user_id: user.login }
+ post :generate_backup_codes, params: { user_id: user.login }
it_redirects_to_with_error(user_path(user), "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
end
it "denies access to other users" do
fake_login
- get :show_backup_codes, params: { user_id: user.login }
+ post :generate_backup_codes, params: { user_id: user.login }
it_redirects_to_with_error(user_path(user), "Sorry, you don't have permission to access the page you were trying to reach.")
end
@@ -158,19 +158,19 @@
it "shows the backup codes once" do
fake_login_known_user(user)
- get :show_backup_codes, params: { user_id: user.login }
+ post :generate_backup_codes, params: { user_id: user.login }
expect(response).to have_http_status(:success)
end
it "denies access to other's pages" do
fake_login_known_user(user)
- get :show_backup_codes, params: { user_id: other_user.login }
+ post :generate_backup_codes, params: { user_id: other_user.login }
it_redirects_to_with_error(user_path(other_user), "Sorry, you don't have permission to access the page you were trying to reach.")
end
it "denies access when TOTP is disabled" do
fake_login_known_user(other_user)
- get :show_backup_codes, params: { user_id: other_user.login }
+ post :generate_backup_codes, params: { user_id: other_user.login }
it_redirects_to_with_error(new_user_totp_path, "Please enable two-step verification first.")
end
end
From 16d6e81b8dc95d91a74cf74a94b0c631508f3b4e Mon Sep 17 00:00:00 2001
From: EchoEkhi
Date: Tue, 26 May 2026 08:24:16 +0100
Subject: [PATCH 08/10] i18n
---
app/views/users/totp/confirm_regenerate_backup_codes.html.erb | 4 ++--
config/locales/views/en.yml | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/views/users/totp/confirm_regenerate_backup_codes.html.erb b/app/views/users/totp/confirm_regenerate_backup_codes.html.erb
index a64d8e90ec7..a0b8a8180f1 100644
--- a/app/views/users/totp/confirm_regenerate_backup_codes.html.erb
+++ b/app/views/users/totp/confirm_regenerate_backup_codes.html.erb
@@ -1,5 +1,5 @@
-
diff --git a/app/views/users/totp/generate_backup_codes.html.erb b/app/views/users/totp/reset_backup_codes.html.erb
similarity index 100%
rename from app/views/users/totp/generate_backup_codes.html.erb
rename to app/views/users/totp/reset_backup_codes.html.erb
diff --git a/config/locales/controllers/en.yml b/config/locales/controllers/en.yml
index 76e9e22d9de..701b5c8a6f6 100644
--- a/config/locales/controllers/en.yml
+++ b/config/locales/controllers/en.yml
@@ -396,7 +396,7 @@ en:
already_enabled: TOTP two-step verification is already enabled.
confirm_disable:
page_title: Disable Two-Step Verification
- confirm_regenerate_backup_codes:
+ confirm_reset_backup_codes:
page_title: Two-Step Verification Backup Codes
create:
incorrect_code: Incorrect verification code. Your code may have expired, or you may need to set up your authenticator app again.
@@ -406,10 +406,10 @@ en:
failure: Could not disable two-step verification.
incorrect_password: Your password was incorrect. Please try again or, if you've forgotten your password, log out and reset your password via the link on the login form.
success: Successfully disabled two-step verification.
- generate_backup_codes:
+ reset_backup_codes:
not_enabled: Please enable two-step verification first.
page_title: Two-Step Verification Backup Codes
- success: Successfully re-generated your backup codes; please make note of your new backup codes, your old backup codes will no longer work.
+ success: Successfully reset your backup codes; please make note of your new backup codes, your old backup codes will no longer work.
new:
page_title: Confirm Password
reauthenticate_create:
diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml
index b5b1c004444..d1a37b5b651 100644
--- a/config/locales/views/en.yml
+++ b/config/locales/views/en.yml
@@ -2593,7 +2593,7 @@ en:
enable_two_step_verification: Enable two-step verification
heading: Account Security
legend: Account Security
- regenerate_backup_codes: Re-generate two-step verification backup codes
+ reset_backup_codes: Reset two-step verification backup codes
browser_page_title_format: Browser page title format
collections_challenges_gifts:
allow_collection_invitation: Allow others to invite my works to collections.
@@ -3250,11 +3250,11 @@ en:
password_label: Password
required_before_disabling: Before disabling two-step verification, you need to verify your password.
submit: Disable Two-Step Verification
- confirm_regenerate_backup_codes:
- confirm: Yes, Re-generate Codes
- confirm_html: Are you sure you want to re-generate your two-step verification backup codes? Your existing backup codes will no longer work.
- page_heading: Re-generate Backup Codes
- generate_backup_codes:
+ confirm_reset_backup_codes:
+ confirm: Yes, Reset Codes
+ confirm_html: Are you sure you want to reset your two-step verification backup codes? Your existing backup codes will no longer work.
+ page_heading: Reset Backup Codes
+ reset_backup_codes:
backup_codes_copied: Backup Codes Copied!
copy_to_clipboard: Copy Backup Codes to Clipboard
finish: Finish
diff --git a/config/routes.rb b/config/routes.rb
index 4c8d53e7e58..a3f387ab786 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -311,8 +311,8 @@
resources :nominations, controller: "tag_set_nominations", only: [:index]
resources :preferences, only: [:index, :update]
resource :totp, controller: "users/totp", only: [:create, :new] do
- get :confirm_regenerate_backup_codes
- post :generate_backup_codes
+ get :confirm_reset_backup_codes
+ post :reset_backup_codes
get :confirm_disable
post :disable
post :reauthenticate_create
diff --git a/features/users/authenticate_users_with_totp.feature b/features/users/authenticate_users_with_totp.feature
index 03e333aba4f..c400ca05fbb 100644
--- a/features/users/authenticate_users_with_totp.feature
+++ b/features/users/authenticate_users_with_totp.feature
@@ -86,9 +86,9 @@ Feature: Authenticate Users With TOTP 2FA
And I am logged in as "user" with password "testpassword"
And user "user" has TOTP 2FA enabled
When I follow "My Preferences"
- And I follow "Re-generate two-step verification backup codes"
- Then I should see "Are you sure you want to re-generate your two-step verification backup codes?"
- When I press "Yes, Regenerate Codes"
+ And I follow "Reset two-step verification backup codes"
+ Then I should see "Are you sure you want to reset your two-step verification backup codes?"
+ When I press "Yes, Reset Codes"
Then I should see "Two-Step Verification Backup Codes"
When I follow "Finish"
Then I should see "Set My Preferences"
diff --git a/spec/controllers/users/totp_controller_spec.rb b/spec/controllers/users/totp_controller_spec.rb
index 42b321a7161..625139d7d7b 100644
--- a/spec/controllers/users/totp_controller_spec.rb
+++ b/spec/controllers/users/totp_controller_spec.rb
@@ -136,18 +136,18 @@
end
end
- describe "POST #generate_backup_codes" do
+ describe "POST #reset_backup_codes" do
let(:user) { create(:user, otp_required_for_login: true) }
let(:other_user) { create(:user) }
it "denies access to guest users" do
- post :generate_backup_codes, params: { user_id: user.login }
+ post :reset_backup_codes, params: { user_id: user.login }
it_redirects_to_with_error(user_path(user), "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
end
it "denies access to other users" do
fake_login
- post :generate_backup_codes, params: { user_id: user.login }
+ post :reset_backup_codes, params: { user_id: user.login }
it_redirects_to_with_error(user_path(user), "Sorry, you don't have permission to access the page you were trying to reach.")
end
@@ -158,19 +158,19 @@
it "shows the backup codes once" do
fake_login_known_user(user)
- post :generate_backup_codes, params: { user_id: user.login }
+ post :reset_backup_codes, params: { user_id: user.login }
expect(response).to have_http_status(:success)
end
it "denies access to other's pages" do
fake_login_known_user(user)
- post :generate_backup_codes, params: { user_id: other_user.login }
+ post :reset_backup_codes, params: { user_id: other_user.login }
it_redirects_to_with_error(user_path(other_user), "Sorry, you don't have permission to access the page you were trying to reach.")
end
it "denies access when TOTP is disabled" do
fake_login_known_user(other_user)
- post :generate_backup_codes, params: { user_id: other_user.login }
+ post :reset_backup_codes, params: { user_id: other_user.login }
it_redirects_to_with_error(new_user_totp_path, "Please enable two-step verification first.")
end
end
From 1718be1036d702296ea1182219d9e4bc4d04341e Mon Sep 17 00:00:00 2001
From: EchoEkhi
Date: Wed, 27 May 2026 05:20:37 +0100
Subject: [PATCH 10/10] i18n
---
config/locales/controllers/en.yml | 8 ++++----
config/locales/views/en.yml | 12 ++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/config/locales/controllers/en.yml b/config/locales/controllers/en.yml
index 701b5c8a6f6..ec9f1e02581 100644
--- a/config/locales/controllers/en.yml
+++ b/config/locales/controllers/en.yml
@@ -406,15 +406,15 @@ en:
failure: Could not disable two-step verification.
incorrect_password: Your password was incorrect. Please try again or, if you've forgotten your password, log out and reset your password via the link on the login form.
success: Successfully disabled two-step verification.
- reset_backup_codes:
- not_enabled: Please enable two-step verification first.
- page_title: Two-Step Verification Backup Codes
- success: Successfully reset your backup codes; please make note of your new backup codes, your old backup codes will no longer work.
new:
page_title: Confirm Password
reauthenticate_create:
incorrect_password: Your password was incorrect. Please try again or, if you've forgotten your password, log out and reset your password via the link on the login form.
page_title: Enable Two-Step Verification
+ reset_backup_codes:
+ not_enabled: Please enable two-step verification first.
+ page_title: Two-Step Verification Backup Codes
+ success: Successfully reset your backup codes; please make note of your new backup codes, your old backup codes will no longer work.
works:
create:
draft_notice_html: Draft was successfully created. It will be %{scheduled_for_deletion_bold} on %{deletion_date}.
diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml
index d1a37b5b651..b4b280734c9 100644
--- a/config/locales/views/en.yml
+++ b/config/locales/views/en.yml
@@ -3254,12 +3254,6 @@ en:
confirm: Yes, Reset Codes
confirm_html: Are you sure you want to reset your two-step verification backup codes? Your existing backup codes will no longer work.
page_heading: Reset Backup Codes
- reset_backup_codes:
- backup_codes_copied: Backup Codes Copied!
- copy_to_clipboard: Copy Backup Codes to Clipboard
- finish: Finish
- page_heading: Two-Step Verification Backup Codes
- write_codes_down: 'Keep these backup codes in a safe and secure place in case you lose access to your authenticator app:'
new:
page_heading: Confirm Password
password_label: Password
@@ -3286,6 +3280,12 @@ en:
please_wait: Please wait...
submit: Enable Two-Step Verification
page_heading: Set Up Two-Step Verification
+ reset_backup_codes:
+ backup_codes_copied: Backup Codes Copied!
+ copy_to_clipboard: Copy Backup Codes to Clipboard
+ finish: Finish
+ page_heading: Two-Step Verification Backup Codes
+ write_codes_down: 'Keep these backup codes in a safe and secure place in case you lose access to your authenticator app:'
works:
adult:
caution: This work could have adult content. If you continue, you have agreed that you are willing to see such content.