reset_password_sent_at assignment for devise - ruby-on-rails

The issue is that devise is checking the reset_password_sent_at when a user clicks on the link to reset their password (from an email) and when trying to reset (in the form) an error occurs:
Reset password token has expired, please request a new one
Meaning, "when a reset_password_token is generated, #user.reset_password_sent_at needs to be set to Time.now, or else when devise runs #user.reset_password_period_valid? to find out if the reset token is still valid, it will get nil and assume the token has expired."
What I don't understand is how and where to assign reset_password_sent_at to Time.now
Do I need to assign Time.now through the console to all Users? If so, How would I do that?
Or, is it a before_create (or something else) that I need to assign Time.now to reset_password_sent_at? If so, how and where should I do this?

You don't need to worry about reset_password_sent_at; that is something devise will take care of setting correctly when a reset_password is sent to the user.
reset_password_sent_at works in conjunction with the reset_password_within parameter, set in the config/initializers/devise.rb file. It should look something like this:
config.reset_password_within = 2.hours
Most likely it is set to nil or 0, and that is making the application throw the password reset token has expired method.

Related

How to skip validation on regeneration of secure token

So I'm sending out password reset e-mails and I've hit a stump on how to solve this issue, so after the user has reset their password. I regenerate a new password reset token (I know this is bad, before I had it generate when I was sending out the password reset request, but that didn't work so I tried this.). Every time I regenerate the token it does not skip validations that I have set in the model.
As said before, I have tried switching it around, regenerating a new token before I send the e-mail and now after they have reset their password. I have also looked the documentation of has_secure_token and I don't see anywhere where I can skip validation when I regenerate a token.
This is my code for resetting the password in the User model.
def reset_password(password)
self.regenerate_reset_password_token
self.password = password
save(:validate => false)
end
I'm getting the error for validating my user's passwords, when regenerating the token, as said before not sure how to skip the validation when regenerating the token on line 2 of the code above.
Validation failed: Password can't be blank, Password is too short (minimum is 8 characters)
You can do
def generate_password_reset_token
self.reset_password_token = generate_token
self.reset_password_sent_at = Time.now
save!
end
def generate_token
SecureRandom.base58(5)
end

Rails devise reset password fails if user record contains invalid data

I have an existing application in production where we have added new mandatory fields to the user record. As a result, the forgot password function is failing. I believe the failure occurs in the following method in recoverable.rb
# Update password saving the record and clearing token. Returns true if
# the passwords are valid and the record was saved, false otherwise.
def reset_password(new_password, new_password_confirmation)
if new_password.present?
self.password = new_password
self.password_confirmation = new_password_confirmation
save
else
errors.add(:password, :blank)
false
end
end
The new attributes cannot be generated mechanically. Only the user himself will know the correct values. Does anyone know of a way to get around this problem?
The only idea that I've come up with so far is as follows:
use a rake script to populate the new fields with values that I know would never occur in real life but would be accepted by the model validation rule
when the user logs in, detect if the user record contains invalid data and force them to do an update. In the user edit form, I would use some javascript to change the bogus values to blank.
You could use a custom validation context to make sure the validations on these added fields are only run when a user edits their profile, but skipped when (for example) Devise saves the user record.
In the User model, add an on:-clause to these validations
validates_presence_of :recently_added_mandatory_field, on: :user_updates_profile
And in the appropriate controller action, add this validation context when calling #valid? or #save:
# Existing code uses `#user.valid?` -> change to:
if #user.valid?(:user_updates_profile)
…
# Existing code uses `#user.save` -> change to:
if #user.save(context: :user_updates_profile)
…
See here for a full description of validation contexts: https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates

Rails3 User Changed Password, how do I change the current session password?

I have the need to let the user change their password. I'd like to switch the password in the session to reflect this new password, without logging them out.
def set_auth username, password
# test username and password here?
auth_object = AuthCookie.new
auth_object.set_username username
auth_object.set_password password
session[:user_login] = auth_object
end
I use something like the above, but it doesn't seem to work in changing the current session's password to the new one the use just entered.
What am I doing wrong?
Don't save your whole auth object in the session, the most important thing is you should not save password info in the session. Rails default session storage is cookie based, just base64 encode string. So if you save user password info in the session, there is security problem.
Just put the user identify in the session, for example, the user_id. session[:user_id] = user_id

Devise allows blank password during password reset

I have a Rails 3.2 app setup with Devise. Everything works great except for when I reset a password ( via the built in routes and methods ), Devise is allowing a blank password. It will validate the length and if it matches the confirmation if there is at least one character. I do have it setup where in a users account they can update their profile without entering the password, but I don't think that has anything to do with resetting the password.
Any help is appreciated.
devise.rb -> http://pastie.org/3911178
user.rb -> http://pastie.org/3911187
Thanks for pointing me in the right direction. The problem was caused by what you described. However, if I let devise handle the validation or use the same code they do, the user must provide a password when updating their account even after they are logged in. To fix this, I just checked for the rest_password_token in my validation:
def password_required?
# If resetting the password
return true if reset_password_token.present? && reset_password_period_valid?
# If the person already has a pass, only validate if they are updating pass
if !encrypted_password.blank?
password.present? || password_confirmation.present?
end
end
*UPDATE
I just updated this to ensure the password token is not expired.
You should let devise handler password validations: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb or use the code devise is using for validations.
The issue with your code is that you're doing validations only if the user doesn't has a password set (!encrypted_password.blank?) and other conditions. When recovering the password the user already has a password set so you don't run validations on password updates ...

OmniAuth and Devise, how to set optional passwords

I am using OmniAuth and Devise to authenticate users. I would like users that have signed up using OmniAuth providers to be able to set an optional password (needed for API authentication) but I'm running into a wall.
If a user creates an account via OmniAuth and tries to set a password they get the following error:
BCrypt::Errors::InvalidHash in RegistrationsController#update
I believe this is because the password is blank. What's a good way around this? I've thought about generating a random password but the problem with that approach is the user needs to know the current password in order to edit settings.
Edit:
I looked at allowing the user to change settings without requiring a current password and that's what I would like to do only if the user didn't have a password initially.
An alternative is to add the following into your 'user' model class to bypass password verification if there is no password to verify, where provider is some field that is set when using external authentication.
def valid_password?(password)
!provider.nil? || super(password)
end
I assume you don't want the easy way out which would be to simply reset the password if they wanted to set it?
user.send_reset_password_instructions
This comes a bit late but it might help someone else, with Andrew's answer you can in create a password and store it in the database, but you can't login using your email and your new password, solved this by setting:
def valid_password
!provider.nil? && !encrypted_password.present? || super
end
Another alternative. You don't have to include a new field. Just catch the exception raised and return false. Here is the code.
def valid_password?(password)
begin
super(password)
rescue BCrypt::Errors::InvalidHash
return false
end
end
This should do the job.

Resources