【Devise】Recoverable
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
今回も一つ前の投稿と同じようにdeviseのモデル設定についての記事となります。
今回はRecoverableについてざっくりとgemの中身を見ながら理解していきます。
概要
モデルやモデルに対応するテーブルでのdeviseの記述は以下のようになります。
- モデル
# app/models/user.rb class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable end
- マイグレーションファイル
class AddDeviseToUsers < ActiveRecord::Migration[5.2] def self.up change_table :users do |t| ・ ・ ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ・ ・
デフォルトの設定で有効になっていますね。ですので機能説明したデータベース認証を行うdatabase_authenticatable
同様に重要な役割を担っていそうです。
マイグレーションファイルをよく見てみましょう。実はRecoverableの機能を推測できます。
## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at
reset_password
とありますね。
そうです。まさにパスワードをリセットする機能を持っています。
ここでモジュールの中身を見ていきましょう。 Recoverableも重要な機能のせいかモジュールの中身がとても長いです。
一番上にある説明ですが、こちらは上述したことと同様の内容を説明しています。
Recoverable takes care of resetting the user password and send reset instructions
訳: Recoverableはユーザーパスワードの再設定を行い、再設定手順を送信します。
概要については以上です。
3つのオプション
概要については理解できたかと思いますが、モジュールの以下の記述はどうでしょう。
# ==Options # # Recoverable adds the following options to devise_for: # # * +reset_password_keys+: the keys you want to use when recovering the password for an account # * +reset_password_within+: the time period within which the password must be reset or the token expires. # * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset.
オプションとあるように追加で設定を加えることができるようですね。 そして、オプションの数は以下の3つです。
reset_password_keys
(パスワードをリセットする際に使用するキー)reset_password_within
(パスワードのリセットの有効期限や、リセットトークンが失効する期限を設定)sign_in_after_reset_password
(パスワードリセット後に自動的にサインインするかどうか)
少し理解がしづらいのはreset_password_keys
の説明かと思います。
こちらはパスワードをリセットする際に使用するモデルのカラムを指定します。 例えば、リセット時に案内のメールを送信するときに、メールアドレスが必要ですね。 そんなときにキーを指定してあげることでモデルのメールアドレスに
オプションの設定方法としてはdevise_for:
とあることから、モデルで設定ができるようですが、多くの記事を見るとinitializerは以下に作成されるdevise.rbに記述するようです。
# devise.rb # パスワードをリセットする際に使用するキーをemailに設定 config.reset_password_keys = [:email] # リセットの有効期限や、リセットトークンが失効する期限を10時間に設定 config.reset_password_within = 10.hours # パスワードリセット後に自動的にサインインすることを許可 config.sign_in_after_reset_password = true
モジュール内
モジュール内で定義されているパスワードのリセットに関するメソッドなどを見ていきましょう。
モジュールの記述の概要を説明している箇所でこのモジュールの機能のExamplesの項目があります。
# == Examples # # # resets the user password and save the record, true if valid passwords are given, otherwise false # User.find(1).reset_password('password123', 'password123') # # # creates a new token and send it with instructions about how to reset the password # User.find(1).
reset_password
1つ目の例はこのモジュールの根幹となるreset_password
メソッドです。
# 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
役割が英語で説明されていますね。
Update password saving the record and clearing token. Returns true if the passwords are valid and the record was saved, false otherwise.
訳:
パスワードを更新しレコードを保存、トークンをクリアします。パスワードが有効でレコードが無事に保存された場合はtrue
を、そうでない場合はfalse
を返します。
第一引数にnew_password
、第二引数にnew_password_confirmation
とあります。これはパスワードを別のパスワードに更新する際に、一度の入力だけでなく確認のためにもう一度入力するという仕様を明示しています。例でも同じパスワードを入力しています。
User.find(1).reset_password('password123', 'password123')
[パスワードリセット画面の例]
new_password
が入力されているのであれば、モデルのpassword
カラムとpassword_confirmation
カラムに、それぞれnew_password
とnew_password_confirmation
を代入し、save
メソッドで保存しています。
if new_password.present? self.password = new_password self.password_confirmation = new_password_confirmation save
send_reset_password_instructions
2つ目はsend_reset_password_instructions
メソッドです。
これもメソッド名から何をしているのかがすぐにわかります。パスワードをリセットする際に自分宛てにメールが送られリセットのフローを説明してくれる機能ですね。
※ 本当に公式のモジュールのメソッドは引数やメソッド名から機能を予測できるようにうまく記載されていて勉強になります!
定義されている箇所は以下になります。
# Resets reset password token and send reset password instructions by email. # Returns the token sent in the e-mail. def send_reset_password_instructions token = set_reset_password_token send_reset_password_instructions_notification(token) token end
Resets reset password token and send reset password instructions by email.
訳: リセットパスワードのトークンと指示メールをリセットします。
まず、set_reset_password_token
メソッドを使用して新しいリセットパスワードのトークンを生成しています。
def set_reset_password_token raw, enc = Devise.token_generator.generate(self.class, :reset_password_token) self.reset_password_token = enc self.reset_password_sent_at = Time.now.utc save(validate: false) raw end
Devise.token_generator
とあるようにトークンを生成してくれる機能があるようです。そしてreset_password_token
カラムに生成したトークンを代入しreset_password_sent_at
カラムには現在時刻を協定世界時 (UTC)で代入します。
Time.now.utc => 2021-03-27 00:06:00 UTC
そして、send_reset_password_instructions_notification
メソッドによってメールを送信していると思われます。下記が定義されている箇所です。
def send_reset_password_instructions_notification(token) send_devise_notification(:reset_password_instructions, token, {}) end
send_devise_notification
メソッドに関しては別のモジュールであるAuthenticatableで定義されているようです。
def send_devise_notification(notification, *args) message = devise_mailer.send(notification, self, *args) # Remove once we move to Rails 4.2+ only. if message.respond_to?(:deliver_now) message.deliver_now else message.deliver end end
message.deliver_now
とあるようまさにメールを送信している箇所ですね。*argsの中にtokenが入っているのでtokenもメールに添付されて送られるのだと思います。これによりパスワードをリセットされるメールがユーザーに届くというわけですね!