heartcombo / devise

Flexible authentication solution for Rails with Warden.
http://blog.plataformatec.com.br/tag/devise/
MIT License
23.85k stars 5.53k forks source link

generate_confirmation_token! is storing 20 char token in db instead of 64 char token in rails 5.0.1 and devise 4.0.0 #5616

Closed yamini0312 closed 11 months ago

yamini0312 commented 11 months ago

Environment

when I use generate_confirmation_token! during signup process, it is storing plaintext confirmation_token with the user record in table. I want to store it as 64 char confirmation_token with user record so, while confirming an email it should match with Devise.token_generator.digest(User, :confirmation_token, original_token). currently this method trying to match the record which has 64 char but in my db it has stored as plaintext so the user record is not matching with confirmation token.

On the previous version of rails 4.2.11 and devise 3.4.0, generate_confirmation_token! was storing 64 char confirmation_token in the user record while signup and it is validating the confirmation_token of user using Devise.token_generator.digest(User, :confirmation_token, original_token) while confirming the email.

Thank you!

carlosantoniodasilva commented 11 months ago

Are you upgrading from Devise 3.4 to 4.0? I don't think anything changed around those versions much, but in 3.1 the way tokens are generated changed, see http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/.

It is possible you were still storing tokens the old way previously if you had any custom implementation that was calling generate_confirmation_token yourself.

If that's not the case, I'm gonna need some more code examples to understand what is happening, because these are pretty old versions. (that we're soon dropping support for) Also, before going to 4.0 I'd recommend upgrading to the latest 3.x version (3.5.10 apparently)

yamini0312 commented 11 months ago

Thank you for the response @carlosantoniodasilva

Here is the logic which i have used. When user Signup, I am sending confirmation email :

def send_confirmation_email(force = false)
    unless @raw_confirmation_token
      if confirmation_token
        @raw_confirmation_token = confirmation_token
      else
        generate_confirmation_token!
        self.save
      end
    end

    opts = pending_reconfirmation? ? { :to => unconfirmed_email } : { }
    opts[:force] = force
   if force == true
      confirmation_token, @raw_confirmation_token = Devise.token_generator.generate(self.class, :confirmation_token)
      if self.update_attributes(confirmation_token: @raw_confirmation_token)
        devise_mailer.send(:forced_confirmation_instructions, self, confirmation_token, opts).deliver
      end
    else
      send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
    end
end

In Devise 4.0.0, above code is storing the plaintext token in the db with user record using generate_confirmation_token! (In devise 3.4.0, it was storing 64 char token).

When user confirm the email by clicking on link, below code excecutes.

def find_unconfirmed_confirmable
    original_token = params[:confirmation_token]
    confirmation_token = Devise.token_generator.digest(User, :confirmation_token, original_token)
    @confirmable = User.find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
    if !@confirmable.new_record?
      @confirmable.only_if_unconfirmed { return @confirmable }
    end
    nil
  end

So here what is happening is, it is trying to match with the 64 char token with user record but everytime it is getting null value because plaintext is stored in db with user record.

Is it safe to store plaintext in the db and compare the plaintext while confirming the email instead of 64 char token?

Thank you!

carlosantoniodasilva commented 11 months ago

Is it safe to store plaintext in the db and compare the plaintext while confirming the email instead of 64 char token?

It should be fine for confirmation. It appears that this changed back on Devise 3.5.2, which I had forgotten: https://github.com/heartcombo/devise/commit/bc6361ab9bd809d8bf51fe2092da6fcb8b495adc.

In other words, we store the plain text generated code for confirmation (but not for reset password), because we no longer sign the user in automatically during the confirmation flow.

In other words, there's probably no need to use the token generator in your custom implementation with the upgrade to v4.0 and above.