Skip to content

Commit 38f783c

Browse files
Rotate confirmation tokens after successful confirmation.
This ensures the token cannot be used again. We have an expiration on this value, but doing a reset is more secure. Issues ------ - Closes #50
1 parent 939709d commit 38f783c

File tree

3 files changed

+7
-1
lines changed

3 files changed

+7
-1
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class User < ApplicationRecord
131131
validates :email, format: {with: URI::MailTo::EMAIL_REGEXP}, presence: true, uniqueness: true
132132

133133
def confirm!
134+
regenerate_confirmation_token
134135
update_columns(confirmed_at: Time.current)
135136
end
136137

@@ -160,6 +161,7 @@ end
160161
> - The `has_secure_password` method is added to give us an [API](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password) to work with the `password_digest` column.
161162
> - The `has_secure_token :confirmation_token` method is added to give us an [API](https://api.rubyonrails.org/classes/ActiveRecord/SecureToken/ClassMethods.html#method-i-has_secure_token) to work with the `confirmation_token` column.
162163
> - The `confirm!` method will be called when a user confirms their email address. We still need to build this feature.
164+
> - Note that we call `regenerate_confirmation_token` to ensure their `confirmation_token` is reset so that it cannot be used again.
163165
> - The `confirmed?` and `unconfirmed?` methods allow us to tell whether a user has confirmed their email address or not.
164166
> - The `confirmation_token_is_valid?` method tells us if the confirmation token is expired or not. This can be controlled by changing the value of the `CONFIRMATION_TOKEN_EXPIRATION_IN_SECONDS` constant. This will be useful when we build the confirmation mailer.
165167
@@ -847,6 +849,7 @@ class User < ApplicationRecord
847849
if unconfirmed_email.present?
848850
return false unless update(email: unconfirmed_email, unconfirmed_email: nil)
849851
end
852+
regenerate_confirmation_token
850853
update_columns(confirmed_at: Time.current)
851854
else
852855
false

app/models/user.rb

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def confirm!
3737
if unconfirmed_email.present?
3838
return false unless update(email: unconfirmed_email, unconfirmed_email: nil)
3939
end
40+
regenerate_confirmation_token
4041
update_columns(confirmed_at: Time.current)
4142
else
4243
false

test/controllers/confirmations_controller_test.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ class ConfirmationsControllerTest < ActionDispatch::IntegrationTest
1010
test "should confirm unconfirmed user" do
1111
freeze_time
1212

13-
get edit_confirmation_path(@unconfirmed_user.confirmation_token)
13+
assert_changes "@unconfirmed_user.reload.confirmation_token" do
14+
get edit_confirmation_path(@unconfirmed_user.confirmation_token)
15+
end
1416

1517
assert_equal Time.current, @unconfirmed_user.reload.confirmed_at
1618
assert @unconfirmed_user.reload.confirmed?

0 commit comments

Comments
 (0)