パスワードリセット機能の実装
ユーザー登録ができるサービスには必ず付いていると言っても過言ではないであろう機能、パスワードリセット機能を実装していきます。
今回はgem sorcery
のモジュールであるreset_password
を導入します。sorceryの導入を前提で進めていきます。
パスワードリセットの流れ
- パスワードリセット申請ページからメールアドレスを送信
- メールを受信しリンクをクリック
- パスワードリセットページでパスワードを更新
という感じです。
開発環境ではメールを実際には送らないようletter_opener_web
というgemを使ってますがそちらは割愛させていただきます。
にではいってみましょう。
reset_passwordモジュールのインストール
$ rails g sorcery:install reset_password --only-submodules
でインストール。
マイグレーションファイルが作成されます。
トークンは一意でなければいけないのでreset_password_tokenカラムにindex制約をかけておきます。
トークンとはワンタイムパスワードのようなものです。
class SorceryResetPassword < ActiveRecord::Migration[5.2] def change add_column :users, :reset_password_token, :string, default: nil add_column :users, :reset_password_token_expires_at, :datetime, default: nil add_column :users, :reset_password_email_sent_at, :datetime, default: nil add_index :users, :reset_password_token # これを追加 end end
として
rails db:migrate
DBに反映。
モデルの方でもvalidationを設定しておきます。
# user.rb validates :reset_password_token, uniqueness: true, allow_nil: true
allow_nilオプション
は、対象の値がnilの場合にバリデーションをスキップします。
パスワードが変更される際にreset_password_tokenは削除されnilになります。
allow_nil: trueがないとユニーク制約にに引っかかってしまうのでこちらも設定します。
Mailerの作成
メイラーとはRailsからメールを送信する機能です。
まずは
$ rails g mailer UserMailer reset_password_email
でUserMailerを作成します。
rails g mailer メーラー名 メソッド名
です。
共通の設定
# application_mailer.rb class ApplicationMailer < ActionMailer::Base default from: 'example@example.com' # メールの送信元 layout 'mailer' # mailer.html.erb、mailer.text.erbをメールのレイアウトとして使用する end
と
メール送信のメソッドを定義します。
# user_mailer.rb 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: 'パスワードリセット' end end
mailメソッド
で送信先、メールのタイトルを設定しています。
sorcery.rbの設定もしておきます。
Rails.application.config.sorcery.submodules = [:reset_password] # サブモジュールとしてreset_passwordを設定 Rails.application.config.sorcery.configure do |config| config.user_config do |user| user.reset_password_mailer = UserMailer # パスワードリセット用のメーラーにUserMailerを指定 end end
メール本文を作成
views/user_mailer/reset_password_email.text.erb
とviews/user_mailer/reset_password_email.html.erb
に実際のメールの内容を記述します。
一部のメールソフトではhtml形式のメールが受け取れない(ガラケーとか?)などがあるようで、その対策としてtext形式とhtml形式の2つで作成します。
Railsではマルチパートメールという仕組みでtext形式とhtml形式の両方をメール送信して、メールクライアント側で自動判別してメールを表示してくれるそうです。
また、メンテナンス等の際、両方ともメンテしなくてもいいようにactionmailer-text
というgemもあるようです。
ここでは取り扱わないので興味のある方は調べてみてください。
コントローラーの設定
# app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController skip_before_action :require_login def create @user = User.find_by_email(params[:email]) @user.deliver_reset_password_instructions! if @user redirect_to(root_path, :notice => 'Instructions have been sent to your email.') end def edit @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end end def update @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end @user.password_confirmation = params[:user][:password_confirmation] if @user.change_password(params[:user][:password]) redirect_to(root_path, :notice => 'Password was successfully updated.') else render :action => "edit" end end end
フォームの作成
パスワードリセット申請フォーム
<%= form_with url: password_resets_path, local: true do |f| %> <%= f.label :email, 'メールアドレス' %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit class: 'btn btn-primary' %> <% end %>
パスワードリセットフォーム
<%= form_with model: @user, url: password_reset_path(@token), local: true do |f| %> <%= f.label :email %><br> <%= @user.email %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit class: 'btn btn-primary ' %> <% end %>
ルーティングの設定
resources :password_resets, only: %i[create edit update]
を追加。 以上です。
最後に
今まで使ったことなかったMailerとかが出てきて難しかったです。
githubからコピペしたのでコントローラ内のコードの流れが正直まだわかってません。
また振り返って流れを追いたいと思います。
間違いなどありましたらコメントいただけると幸いです。
読んでいただきありがとうございました。