heartcombo / devise

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

common 'issue' with Devise Recoverable, password reset loop #5688

Open jbeyer05 opened 6 months ago

jbeyer05 commented 6 months ago

In applications where I use Devise, I have seen situations where users run into an issue with the 'Forgot Password' functionality, which is partly user error, but which also could be easily fixed with a simple change to the workflow. And I'm wondering if I've somehow missed something in the documentation that might allow for a configuration setting that would fix the issue.

Fairly frequently, users will generate a password reset link, lack any patience when the email doesn't immediately arrive, and request a second link. Then the first email arrives and they follow that link. The reset token in the first email has been invalidated by the second request. But they open that link, and request a reset link a third time. They now see the second email, and follow the second link, which is now invalid as well. And the loop repeats.

Am I missing a configuration option? It seems like I would need to roll a semi-custom solution to make this work, and Google isn't turning up any results on simple tutorials to do this.

It seems like Recoverable should have an option to NOT modify the reset password token within a timeframe specified via configuration (maybe 10 minutes). It wouldn't seem to have any security drawbacks that I can see.

jbeyer05 commented 6 months ago

If I were to create a PR with a new configuration option for Recoverable that took a time period and didn't reset the token within that window, is that something that the maintainers of the project would be interested in incorporating?

timdiggins commented 5 months ago

I think this is a great idea (this is a loop I'm aware of). Devise should still send an email though - it should say something like - please see the earlier email for the reset password link or wait 10 minutes and try again.

jbeyer05 commented 5 months ago

Devise should still send an email though - it should say something like - please see the earlier email for the reset password link or wait 10 minutes and try again.

I think the email can be exactly the same as the initial email. It just doesn't change the password reset token, so that the links in either the first, second or tenth email still works. After some period of time, a new request for a password reset token should actually provide a new token. But I don't see a security issue with reusing the token a few times to prevent this infinite loop.

faelsoto commented 3 months ago

Here's our solution, it will not trigger the same email but it can be easily implemented through Rails.cache.

Devise::Models::Recoverable.module_eval do
  alias_method :original_send_reset_password_instructions_notification, :send_reset_password_instructions_notification
  alias_method :original_set_reset_password_token, :set_reset_password_token

  def set_reset_password_token
    return if reset_password_sent_at.present? && !reset_password_sent_at.before?(3.minutes.ago)

    original_set_reset_password_token
  end

  protected

  def send_reset_password_instructions_notification(*args)
    return if args.first.blank?

    original_send_reset_password_instructions_notification(*args)
  end
end