John Bachir
John Bachir

Reputation: 22731

How can I make Devise immediately inform user of expired password reset token?

If a user requests 2 password reset links in a row, then only the most recent one is valid.

If a user clicks on the older one, they are not immediately informed of the token being invalid. They are asked to type in the new password twice. Only upon hitting submit are they informed that the token is not valid.

Is there a standard way to change this behavior, so the user is immediately informed that they used the wrong link?

Upvotes: 0

Views: 1565

Answers (2)

Ismail Akbudak
Ismail Akbudak

Reputation: 305

There is another safety solution for this question;

class User::PasswordsController < Devise::PasswordsController
  def update
    super do |resource|
      # Jump to super code
      next unless resource.errors.any?
      token_errors = resource.errors.details[:reset_password_token]
      expired_error = token_errors.select { |detail| detail[:error] == :expired }
      # Jump to super code
      next unless expired_error.present?
      message = resource.errors.full_messages_for(:reset_password_token).join(',')
      return redirect_to new_user_password_path, alert: message
    end
  end
end

Upvotes: 0

Zach Kemp
Zach Kemp

Reputation: 11904

I was a bit surprised to find out that this is standard behavior - thanks for bringing it up! I would probably use a custom controller that inherits from Devise::PasswordsController and simply override :edit. The trouble is we're working with an abstract user (since no user is actually authenticated at this point), so we can't check it against a particular token in the database, and we have to check whether the given token exists.

class PasswordsController < Devise::PasswordsController
  before_filter :validate_reset_password_token, only: :edit

  private
    def validate_reset_password_token
      recoverable = resource_class.find_by_reset_password_token(params[:reset_password_token])
      redirect_to root_path unless (recoverable && recoverable.reset_password_period_valid?)
    end
end

This will redirect unless a user exists with the token passed. You can handle the redirection/error display however you wish.

Finally, in your routes file, change the controller used by devise for passwords:

devise_for :users, :controllers => { passwords: "passwords" }

Caveats:

  • I have not tested this (but I might look into it tomorrow).
  • There may be some additional security issues to consider - could someone realistically gain access to a random account? (probably not; Devise uses a similar method of locating a model instance using the reset token in reset_password_by_token)

By the way, there is no way to tell them that they used the wrong link and they should click the other one, as there is no way to positively identify them in the first place, and therefore no way to know that they have already generated another valid token.

Upvotes: 4

Related Questions