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_params
を reset_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')
を実行して、 出てきた値を データベース に登録すればいいことになります。