LoginSignup
4
0

Devise パスワードリセットの照合プロセス - トークンはどのように照合されるのか

Last updated at Posted at 2020-06-11

Rails devise でパスワードリセットを行う際のトークンの照合プロセスは次のようになっています。
掲載している devise のコードは 2020-06-11時点での Devise のマスターブランチです。

パスワードをリセットする際に、メールでトークン付きのURLが送られます。
パスワードのリセットは、次のようにコンソールで実行すると行われ、メールが送信されてトークンが生成・保存されます。

u = User.find(XX)
u.send_reset_password_instructions

トークン付きのURLを開いてパスワードをリセットするフォームを送信すると、複雑なカスタマイズをしていない限り、 次の update メソッド が実行されます。
ここで resource_params は 次のようなパラメータ(入力値)です。

{"reset_password_token"=>"xxx_token_xxx", "password"=>"password", "password_confirmation"=>"password"}

class Devise::PasswordsController < DeviseController
  # PUT /resource/password
  def update
    self.resource = resource_class.reset_password_by_token(resource_params)
    yield resource if block_given?

    if resource.errors.empty?
      resource.unlock_access! if unlockable?(resource)
      if Devise.sign_in_after_reset_password
        flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
        set_flash_message!(:notice, flash_message)
        resource.after_database_authentication
        sign_in(resource_name, resource)
      else
        set_flash_message!(:notice, :updated_not_active)
      end
      respond_with resource, location: after_resetting_password_path_for(resource)
    else
      set_minimum_password_length
      respond_with resource
    end
  end

update メソッド ではこの resource_paramsreset_password_by_token 関数 に渡して、 その中でトークンの照合を行います。

URLについていたトークンから Devise.token_generator.digest によってダイジェスト値が生成されます。
その値がデータベースの reset_password_token の値で、 それをキーにして resource を探します。

module Devise
  module Models
    module Recoverable
      module ClassMethods

        # Attempt to find a user by its reset_password_token to reset its
        # password. If a user is found and token is still valid, reset its password and automatically
        # try saving the record. If not user is found, returns a new user
        # containing an error in reset_password_token attribute.
        # Attributes must contain reset_password_token, password and confirmation
        def reset_password_by_token(attributes={})
          original_token       = attributes[:reset_password_token]
          reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)

          recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)

          if recoverable.persisted?
            if recoverable.reset_password_period_valid?
              recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
            else
              recoverable.errors.add(:reset_password_token, :expired)
            end
          end

          recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
          recoverable
        end

たとえば、 token の値を ABC にしたかったら(ユーザが使用するトークンをABCにしたかったら)、 Devise.token_generator.digest(User, :reset_password_token, 'ABC') を実行して、 出てきた値を データベース に登録すればいいことになります。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0