@@ -7,7 +7,7 @@ module Models
77 #
88 # Confirmable tracks the following columns:
99 #
10- # * confirmation_token - An OpenSSL::HMAC.hexdigest of @raw_confirmation_token
10+ # * confirmation_token - A unique random token
1111 # * confirmed_at - A timestamp when the user clicked the confirmation link
1212 # * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
1313 # * unconfirmed_email - An email address copied from the email attr. After confirmation
@@ -29,6 +29,8 @@ module Models
2929 # confirmation.
3030 # * +confirm_within+: the time before a sent confirmation token becomes invalid.
3131 # You can use this to force the user to confirm within a set period of time.
32+ # Confirmable will not generate a new token if a repeat confirmation is requested
33+ # during this time frame, unless the user's email changed too.
3234 #
3335 # == Examples
3436 #
@@ -230,10 +232,13 @@ def pending_any_confirmation
230232 # Generates a new random token for confirmation, and stores
231233 # the time this token is being generated in confirmation_sent_at
232234 def generate_confirmation_token
233- raw , enc = Devise . token_generator . generate ( self . class , :confirmation_token )
234- @raw_confirmation_token = raw
235- self . confirmation_token = enc
236- self . confirmation_sent_at = Time . now . utc
235+ if self . confirmation_token && !confirmation_period_expired?
236+ @raw_confirmation_token = self . confirmation_token
237+ else
238+ raw , _ = Devise . token_generator . generate ( self . class , :confirmation_token )
239+ self . confirmation_token = @raw_confirmation_token = raw
240+ self . confirmation_sent_at = Time . now . utc
241+ end
237242 end
238243
239244 def generate_confirmation_token!
@@ -244,6 +249,7 @@ def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
244249 @reconfirmation_required = true
245250 self . unconfirmed_email = self . email
246251 self . email = self . email_was
252+ self . confirmation_token = nil
247253 generate_confirmation_token
248254 end
249255
@@ -293,12 +299,17 @@ def send_confirmation_instructions(attributes={})
293299 # If the user is already confirmed, create an error for the user
294300 # Options must have the confirmation_token
295301 def confirm_by_token ( confirmation_token )
296- original_token = confirmation_token
297- confirmation_token = Devise . token_generator . digest ( self , :confirmation_token , confirmation_token )
302+ confirmable = find_first_by_auth_conditions ( confirmation_token : confirmation_token )
303+ unless confirmable
304+ confirmation_digest = Devise . token_generator . digest ( self , :confirmation_token , confirmation_token )
305+ confirmable = find_or_initialize_with_error_by ( :confirmation_token , confirmation_digest )
306+ end
307+
308+ # TODO: replace above lines with
309+ # confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
310+ # after enough time has passed that Devise clients do not use digested tokens
298311
299- confirmable = find_or_initialize_with_error_by ( :confirmation_token , confirmation_token )
300312 confirmable . confirm if confirmable . persisted?
301- confirmable . confirmation_token = original_token
302313 confirmable
303314 end
304315
0 commit comments