Do not use digests for confirmation tokens
This commit is contained in:
parent
e538f02f30
commit
eb640ed344
|
@ -7,7 +7,7 @@ module Devise
|
||||||
#
|
#
|
||||||
# Confirmable tracks the following columns:
|
# Confirmable tracks the following columns:
|
||||||
#
|
#
|
||||||
# * confirmation_token - An OpenSSL::HMAC.hexdigest of @raw_confirmation_token
|
# * confirmation_token - A unique random token
|
||||||
# * confirmed_at - A timestamp when the user clicked the confirmation link
|
# * confirmed_at - A timestamp when the user clicked the confirmation link
|
||||||
# * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
|
# * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
|
||||||
# * unconfirmed_email - An email address copied from the email attr. After confirmation
|
# * unconfirmed_email - An email address copied from the email attr. After confirmation
|
||||||
|
@ -29,6 +29,8 @@ module Devise
|
||||||
# confirmation.
|
# confirmation.
|
||||||
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
||||||
# You can use this to force the user to confirm within a set period of time.
|
# You can use this to force the user to confirm within a set period of time.
|
||||||
|
# Confirmable will not generate a new token if a repeat confirmation is requested
|
||||||
|
# during this time frame, unless the user's email changed too.
|
||||||
#
|
#
|
||||||
# == Examples
|
# == Examples
|
||||||
#
|
#
|
||||||
|
@ -230,10 +232,13 @@ module Devise
|
||||||
# Generates a new random token for confirmation, and stores
|
# Generates a new random token for confirmation, and stores
|
||||||
# the time this token is being generated in confirmation_sent_at
|
# the time this token is being generated in confirmation_sent_at
|
||||||
def generate_confirmation_token
|
def generate_confirmation_token
|
||||||
raw, enc = Devise.token_generator.generate(self.class, :confirmation_token)
|
if self.confirmation_token && !confirmation_period_expired?
|
||||||
@raw_confirmation_token = raw
|
@raw_confirmation_token = self.confirmation_token
|
||||||
self.confirmation_token = enc
|
else
|
||||||
self.confirmation_sent_at = Time.now.utc
|
raw, _ = Devise.token_generator.generate(self.class, :confirmation_token)
|
||||||
|
self.confirmation_token = @raw_confirmation_token = raw
|
||||||
|
self.confirmation_sent_at = Time.now.utc
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_confirmation_token!
|
def generate_confirmation_token!
|
||||||
|
@ -244,6 +249,7 @@ module Devise
|
||||||
@reconfirmation_required = true
|
@reconfirmation_required = true
|
||||||
self.unconfirmed_email = self.email
|
self.unconfirmed_email = self.email
|
||||||
self.email = self.email_was
|
self.email = self.email_was
|
||||||
|
self.confirmation_token = nil
|
||||||
generate_confirmation_token
|
generate_confirmation_token
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -293,12 +299,17 @@ module Devise
|
||||||
# If the user is already confirmed, create an error for the user
|
# If the user is already confirmed, create an error for the user
|
||||||
# Options must have the confirmation_token
|
# Options must have the confirmation_token
|
||||||
def confirm_by_token(confirmation_token)
|
def confirm_by_token(confirmation_token)
|
||||||
original_token = confirmation_token
|
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
|
||||||
confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
unless confirmable
|
||||||
|
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
||||||
|
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: replace above lines with
|
||||||
|
# confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
||||||
|
# after enough time has passed that Devise clients do not use digested tokens
|
||||||
|
|
||||||
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
|
||||||
confirmable.confirm if confirmable.persisted?
|
confirmable.confirm if confirmable.persisted?
|
||||||
confirmable.confirmation_token = original_token
|
|
||||||
confirmable
|
confirmable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class ConfirmationInstructionsTest < ActionMailer::TestCase
|
||||||
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
|
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
|
||||||
|
|
||||||
if mail.body.encoded =~ %r{<a href=\"http://#{host}:#{port}/users/confirmation\?confirmation_token=([^"]+)">}
|
if mail.body.encoded =~ %r{<a href=\"http://#{host}:#{port}/users/confirmation\?confirmation_token=([^"]+)">}
|
||||||
assert_equal Devise.token_generator.digest(user.class, :confirmation_token, $1), user.confirmation_token
|
assert_equal $1, user.confirmation_token
|
||||||
else
|
else
|
||||||
flunk "expected confirmation url regex to match"
|
flunk "expected confirmation url regex to match"
|
||||||
end
|
end
|
||||||
|
|
|
@ -291,12 +291,23 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'always generate a new token on resend' do
|
test 'do not generate a new token on resend' do
|
||||||
user = create_user
|
user = create_user
|
||||||
old = user.confirmation_token
|
old = user.confirmation_token
|
||||||
user = User.find(user.id)
|
user = User.find(user.id)
|
||||||
user.resend_confirmation_instructions
|
user.resend_confirmation_instructions
|
||||||
assert_not_equal user.confirmation_token, old
|
assert_equal user.confirmation_token, old
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'generate a new token after first has expired' do
|
||||||
|
swap Devise, confirm_within: 3.days do
|
||||||
|
user = create_user
|
||||||
|
old = user.confirmation_token
|
||||||
|
user.update_attribute(:confirmation_sent_at, 4.days.ago)
|
||||||
|
user = User.find(user.id)
|
||||||
|
user.resend_confirmation_instructions
|
||||||
|
assert_not_equal user.confirmation_token, old
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'should call after_confirmation if confirmed' do
|
test 'should call after_confirmation if confirmed' do
|
||||||
|
|
Loading…
Reference in New Issue