reset_passwordモジュールを使ってパスワードリセット機能を実装【sorcery】

はじめに

sorceryのreset_passwordモジュールを使って、パスワードリセット機能を実装します。

設定方法

まずは、sorceryのパスワードリセットモジュールを導入します。

$ rails g sorcery:install reset_password --only-submodules

パスワードリセットに関するマイグレーションファイルが作成されるので、rails db:migrateします。

続いて、バリデーションを追加します。

validates :reset_password_token, uniqueness: true, allow_nil: true

トークンを一意にすることと、更新する際にトークンを削除するのでnilでもOKにします。

コントローラを作成します。

$ rails g controller PasswordResets new create edit update
class PasswordResetsController < ApplicationController
   skip_before_action :require_login
 
   def new; end
 
   def create
     @user = User.find_by(email: params[:email])
     @user&.deliver_reset_password_instructions!  #メールを送信

     # 「存在しないメールアドレスです」という旨の文言を表示すると、逆に存在するメールアドレスを特定されてしまうため、
     # あえて成功時のメッセージを送信させている
     redirect_to login_path, success: 'パスワードリセット手順を送信しました'
   end
 
   def edit
     @token = params[:id]
     @user = User.load_from_reset_password_token(@token)
     # トークンでユーザーを検索して取得する。
     # トークンの有効期限も確認

     not_authenticated if @user.blank?
   end
 
   def update
     @token = params[:id]
     @user = User.load_from_reset_password_token(@token)
 
     return not_authenticated if @user.blank?
 
     @user.password_confirmation = params[:user][:password_confirmation]
      # パスワード確認のバリデーションの確認

     if @user.change_password(params[:user][:password])
        # トークンを削除してパスワードを更新

       redirect_to login_path, success: 'パスワードを変更しました'
     else
       flash.now[:danger] = 'パスワードを更新できませんでした'
       render :edit
     end
   end
 end

ルーティングを追加します。

resources :password_resets, only: %i[new create edit update]

続いてユーザーメイラーを作成します。

$ rails g mailer UserMailer reset_password_email

ユーザーにメールを送信するメソッドを定義します。

  class UserMailer < ApplicationMailer
 
   def reset_password_email(user)
     @user = User.find(user.id)
     @url  = edit_password_reset_url(@user.reset_password_token)
     mail(to: user.email,
          subject: t('defaults.password_reset'))
   end
  end

メールの本文を作成します。

 <p><%= @user.decorate.full_name %>様</p>
 <p>===============================================</p>
 
 <p>パスワード再発行のご依頼を受け付けました。</p>
 
 <p>こちらのリンクからパスワードの再発行を行ってください。</p>
 <p><a href="<%= @url %>"><%= @url %></a></p>
 <%= @user.decorate.full_name %>様
 ===============================================
 
 パスワード再発行のご依頼を受け付けました。
 
 こちらのリンクからパスワードの再発行を行ってください。
 <%= @url %>

パスワードリセットの申請画面と更新画面のビューを作成します。

そして、開発環境では実際にメールは送らないようにするためにletter_opener_webというGemを使います。

group :development do
  gem 'letter_opener_web'
end

letter_opener_webの設定をします。

# routes.rb

mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?

# config/environments/development.rb

config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_mailer.delivery_method = :letter_opener_web

続いて、環境ごとに異なる定数を管理するためconfigを追加します。

gem 'config'

設定ファイルを生成します。

$ rails g config:install
# config/settings/development.yml

host: 'localhost:3000'

# config/environments/development.rb

config.action_mailer.default_url_options = { host: Settings.host }