Skip to content

Commit fee43f3

Browse files
authored
Always return an error when confirmation_token is blank (#5132)
As reported in #5071, if for some reason, a user in the database had the `confirmation_token` column as a blank string, Devise would confirm that user after receiving a request with a blank `confirmation_token` parameter. After this commit, a request sending a blank `confirmation_token` parameter will receive a validation error. For applications that have users with a blank `confirmation_token` in the database, it's recommended to manually regenerate or to nullify them.
1 parent fad6074 commit fee43f3

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

lib/devise/models/confirmable.rb

+12
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,19 @@ def send_confirmation_instructions(attributes={})
348348
# If the user is already confirmed, create an error for the user
349349
# Options must have the confirmation_token
350350
def confirm_by_token(confirmation_token)
351+
# When the `confirmation_token` parameter is blank, if there are any users with a blank
352+
# `confirmation_token` in the database, the first one would be confirmed here.
353+
# The error is being manually added here to ensure no users are confirmed by mistake.
354+
# This was done in the model for convenience, since validation errors are automatically
355+
# displayed in the view.
356+
if confirmation_token.blank?
357+
confirmable = new
358+
confirmable.errors.add(:confirmation_token, :blank)
359+
return confirmable
360+
end
361+
351362
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
363+
352364
unless confirmable
353365
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
354366
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)

test/integration/confirmable_test.rb

+30
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,36 @@ def resend_confirmation
175175
assert_current_url '/users/sign_in'
176176
end
177177

178+
test "should not be able to confirm an email with a blank confirmation token" do
179+
visit_user_confirmation_with_token("")
180+
181+
assert_contain "Confirmation token can't be blank"
182+
end
183+
184+
test "should not be able to confirm an email with a nil confirmation token" do
185+
visit_user_confirmation_with_token(nil)
186+
187+
assert_contain "Confirmation token can't be blank"
188+
end
189+
190+
test "should not be able to confirm user with blank confirmation token" do
191+
user = create_user(confirm: false)
192+
user.update_attribute(:confirmation_token, "")
193+
194+
visit_user_confirmation_with_token("")
195+
196+
assert_contain "Confirmation token can't be blank"
197+
end
198+
199+
test "should not be able to confirm user with nil confirmation token" do
200+
user = create_user(confirm: false)
201+
user.update_attribute(:confirmation_token, nil)
202+
203+
visit_user_confirmation_with_token(nil)
204+
205+
assert_contain "Confirmation token can't be blank"
206+
end
207+
178208
test 'error message is configurable by resource name' do
179209
store_translations :en, devise: {
180210
failure: { user: { unconfirmed: "Not confirmed user" } }

test/models/confirmable_test.rb

+18
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ def setup
7777
assert_equal "can't be blank", confirmed_user.errors[:confirmation_token].join
7878
end
7979

80+
test 'should return a new record with errors when a blank token is given and a record exists on the database' do
81+
user = create_user(confirmation_token: '')
82+
83+
confirmed_user = User.confirm_by_token('')
84+
85+
refute user.reload.confirmed?
86+
assert_equal "can't be blank", confirmed_user.errors[:confirmation_token].join
87+
end
88+
89+
test 'should return a new record with errors when a nil token is given and a record exists on the database' do
90+
user = create_user(confirmation_token: nil)
91+
92+
confirmed_user = User.confirm_by_token(nil)
93+
94+
refute user.reload.confirmed?
95+
assert_equal "can't be blank", confirmed_user.errors[:confirmation_token].join
96+
end
97+
8098
test 'should generate errors for a user email if user is already confirmed' do
8199
user = create_user
82100
user.confirmed_at = Time.now

0 commit comments

Comments
 (0)