From 84213265e7ca3481409583747da73d4d04915931 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Wed, 3 Dec 2025 15:37:27 +0100 Subject: [PATCH 1/5] Render friendly error message when using an invalid token --- .../email_addresses/confirmations_controller.rb | 12 +++++++----- app/models/user/email_address_changeable.rb | 14 +++++--------- .../confirmations/invalid_token.html.erb | 16 ++++++++++++++++ .../confirmations_controller_test.rb | 11 ++++++----- .../models/user/email_address_changeable_test.rb | 15 ++++++++------- 5 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 app/views/users/email_addresses/confirmations/invalid_token.html.erb diff --git a/app/controllers/users/email_addresses/confirmations_controller.rb b/app/controllers/users/email_addresses/confirmations_controller.rb index dad998671..b5fd334ea 100644 --- a/app/controllers/users/email_addresses/confirmations_controller.rb +++ b/app/controllers/users/email_addresses/confirmations_controller.rb @@ -8,12 +8,14 @@ class Users::EmailAddresses::ConfirmationsController < ApplicationController end def create - user = @user.change_email_address_using_token(token) + if @user.change_email_address_using_token(token) + terminate_session if Current.session + start_new_session_for @user.identity - terminate_session if Current.session - start_new_session_for user.identity - - redirect_to edit_user_url(script_name: user.account.slug, id: user) + redirect_to edit_user_url(script_name: @user.account.slug, id: @user) + else + render :invalid_token, status: :unprocessable_entity + end end private diff --git a/app/models/user/email_address_changeable.rb b/app/models/user/email_address_changeable.rb index ce18d46d0..2c8de7217 100644 --- a/app/models/user/email_address_changeable.rb +++ b/app/models/user/email_address_changeable.rb @@ -7,14 +7,12 @@ module User::EmailAddressChangeable def change_email_address_using_token(token) parsed_token = SignedGlobalID.parse(token, for: EMAIL_CHANGE_TOKEN_PURPOSE) - if parsed_token.nil? - raise ArgumentError, "The token is invalid" - elsif parsed_token.find != self - raise ArgumentError, "The token was generated for a different user" - elsif identity.email_address != parsed_token.params.fetch("old_email_address") - raise ArgumentError, "The token was generated for a different email address" + old_email_address = parsed_token&.params&.fetch("old_email_address") + new_email_address = parsed_token&.params&.fetch("new_email_address") + + if parsed_token.nil? || parsed_token.find != self || identity.email_address != old_email_address + false else - new_email_address = parsed_token.params.fetch("new_email_address") change_email_address(new_email_address) end end @@ -37,8 +35,6 @@ module User::EmailAddressChangeable new_identity = Identity.find_or_create_by!(email_address: new_email_address) update!(identity: new_identity) end - - self end private diff --git a/app/views/users/email_addresses/confirmations/invalid_token.html.erb b/app/views/users/email_addresses/confirmations/invalid_token.html.erb new file mode 100644 index 000000000..13e66c97d --- /dev/null +++ b/app/views/users/email_addresses/confirmations/invalid_token.html.erb @@ -0,0 +1,16 @@ +<% @page_title = "Confirm email change" %> + +
+
+

+ <%= @page_title %> +

+

Something went wrong.

+
+ +

+ It looks like the email confirmation link is no longer valid. + Try changing your email address again from <%= link_to "your profile", edit_user_url(script_name: @user.account.slug, id: @user) %>. + If that doesn't help, please <%= mail_to "send us an email", "support@fizzy.do" %>. +

+
diff --git a/test/controllers/users/email_addresses/confirmations_controller_test.rb b/test/controllers/users/email_addresses/confirmations_controller_test.rb index b16b6ea7c..a68cfd175 100644 --- a/test/controllers/users/email_addresses/confirmations_controller_test.rb +++ b/test/controllers/users/email_addresses/confirmations_controller_test.rb @@ -3,6 +3,7 @@ require "test_helper" class Users::EmailAddresses::ConfirmationsControllerTest < ActionDispatch::IntegrationTest setup do @user = users(:david) + @old_email = @user.identity.email_address @new_email = "newemail@example.com" @token = @user.send(:generate_email_address_change_token, to: @new_email) end @@ -13,8 +14,6 @@ class Users::EmailAddresses::ConfirmationsControllerTest < ActionDispatch::Integ end test "create" do - old_email = @user.identity.email_address - post user_email_address_confirmation_path(user_id: @user.id, email_address_token: @token, script_name: @user.account.slug) assert_equal @new_email, @user.reload.identity.email_address @@ -22,8 +21,10 @@ class Users::EmailAddresses::ConfirmationsControllerTest < ActionDispatch::Integ end test "create with invalid token" do - assert_raises(ArgumentError) do - post user_email_address_confirmation_path(user_id: @user.id, email_address_token: "invalid", script_name: @user.account.slug) - end + post user_email_address_confirmation_path(user_id: @user.id, email_address_token: "invalid", script_name: @user.account.slug) + + assert_equal @user.identity.email_address, @old_email + assert_response :unprocessable_entity + assert_match /Something went wrong/, response.body end end diff --git a/test/models/user/email_address_changeable_test.rb b/test/models/user/email_address_changeable_test.rb index 5e1158cf3..951170297 100644 --- a/test/models/user/email_address_changeable_test.rb +++ b/test/models/user/email_address_changeable_test.rb @@ -7,6 +7,7 @@ class User::EmailAddressChangeableTest < ActiveSupport::TestCase @identity = identities(:kevin) @user = @identity.users.find_by!(account: accounts("37s")) @new_email = "newart@example.com" + @old_email = @identity.email_address end test "send_email_address_change_confirmation" do @@ -42,15 +43,15 @@ class User::EmailAddressChangeableTest < ActiveSupport::TestCase end test "change_email_address_using_token with invalid token" do - assert_raises(ArgumentError, match: /invalid/) do - @user.change_email_address_using_token("invalid_token") - end + assert_not @user.change_email_address_using_token("invalid_token") + assert_equal @old_email, @user.reload.identity.email_address token = @user.send(:generate_email_address_change_token, to: @new_email) - @identity.update!(email_address: "different@example.com") + old_email = "#{SecureRandom.hex(16)}@example.com" + @identity.update!(email_address: old_email) + @user.reload - assert_raises(ArgumentError, match: /different email address/) do - @user.change_email_address_using_token(token) - end + assert_not @user.change_email_address_using_token(token) + assert_equal old_email, @user.reload.identity.email_address end end From 92b7c4a86ce3a4f9af3da320e53ec702d03d22a3 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Wed, 3 Dec 2025 15:44:20 +0100 Subject: [PATCH 2/5] Update copy --- .../confirmations/invalid_token.html.erb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/views/users/email_addresses/confirmations/invalid_token.html.erb b/app/views/users/email_addresses/confirmations/invalid_token.html.erb index 13e66c97d..fb62fbda7 100644 --- a/app/views/users/email_addresses/confirmations/invalid_token.html.erb +++ b/app/views/users/email_addresses/confirmations/invalid_token.html.erb @@ -8,9 +8,12 @@

Something went wrong.

-

- It looks like the email confirmation link is no longer valid. - Try changing your email address again from <%= link_to "your profile", edit_user_url(script_name: @user.account.slug, id: @user) %>. - If that doesn't help, please <%= mail_to "send us an email", "support@fizzy.do" %>. +

+ It looks like this email confirmation link is no longer valid. They last only 30 minues. + Try changing your email address again from <%= link_to "your profile", edit_user_url(script_name: @user.account.slug, id: @user) %>. +

+ +

+ If that doesn't help, please <%= mail_to "support@fizzy.do", "send us an email" %>.

From a6c5cc19b7edcacd8b1545b486333bc29e2c4247 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 3 Dec 2025 11:21:13 -0600 Subject: [PATCH 3/5] Copy and layout edits for the new invalid token screen --- .../confirmations/invalid_token.html.erb | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/views/users/email_addresses/confirmations/invalid_token.html.erb b/app/views/users/email_addresses/confirmations/invalid_token.html.erb index fb62fbda7..ec3950efb 100644 --- a/app/views/users/email_addresses/confirmations/invalid_token.html.erb +++ b/app/views/users/email_addresses/confirmations/invalid_token.html.erb @@ -1,19 +1,16 @@ -<% @page_title = "Confirm email change" %> +<% @page_title = "Link expired" %> -
-
-

- <%= @page_title %> -

-

Something went wrong.

-
- -

- It looks like this email confirmation link is no longer valid. They last only 30 minues. - Try changing your email address again from <%= link_to "your profile", edit_user_url(script_name: @user.account.slug, id: @user) %>. +

+

+ <%= @page_title %> +

+

+ That email confirmation link is no longer valid—they expire after 30 minues. You’ll have to try again.

-

- If that doesn't help, please <%= mail_to "support@fizzy.do", "send us an email" %>. + <%= link_to "Change my email address", new_user_email_address_path(user_id: @user, script_name: @user.account.slug), class: "btn btn--link center" %> + +

+ If you get stuck, <%= mail_to "support@fizzy.do", "send us an email" %> and we’ll get you back on track.

From df04fb92504fb194a09d423715d006e0e4078760 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 3 Dec 2025 11:21:34 -0600 Subject: [PATCH 4/5] Adjust for layout consistency and tighten up some copy --- app/views/users/email_addresses/confirmations/show.html.erb | 4 ++-- app/views/users/email_addresses/create.html.erb | 4 ++-- app/views/users/email_addresses/new.html.erb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/users/email_addresses/confirmations/show.html.erb b/app/views/users/email_addresses/confirmations/show.html.erb index ed6e24f72..15347e887 100644 --- a/app/views/users/email_addresses/confirmations/show.html.erb +++ b/app/views/users/email_addresses/confirmations/show.html.erb @@ -1,11 +1,11 @@ <% @page_title = "Confirm email change" %> -
+

<%= @page_title %>

-

Just a sec while we confirm your new email address.

+

Just a sec while we confirm your new email address.

<%= form_with url: user_email_address_confirmation_path(user_id: @user.id), method: :post, data: { controller: "form auto-submit" } do |form| %> diff --git a/app/views/users/email_addresses/create.html.erb b/app/views/users/email_addresses/create.html.erb index 0ed95d7cc..00b660457 100644 --- a/app/views/users/email_addresses/create.html.erb +++ b/app/views/users/email_addresses/create.html.erb @@ -6,10 +6,10 @@
<% end %> -
+

Check your email

We just sent an email to <%= params[:email_address] %>

-

Hit the link in the email to confirm this is the email address you want to use with Fizzy going forward.

+

Hit the link in the email to confirm this is the email address you want to use with Fizzy going forward.

<%= link_to "Done", edit_user_path(@user, script_name: @user.account.slug), class: "btn btn--link center" %>
diff --git a/app/views/users/email_addresses/new.html.erb b/app/views/users/email_addresses/new.html.erb index 397e62300..589dca937 100644 --- a/app/views/users/email_addresses/new.html.erb +++ b/app/views/users/email_addresses/new.html.erb @@ -6,7 +6,7 @@
<% end %> -
+

<%= @page_title %>

@@ -18,7 +18,7 @@
-

Enter your new email address, then hit the link to confirm it

+

Enter your new email address, then check your email to confirm the change.