パスワードリセット実装までの流れ
パスワードリセットの工程は長いので、どんな工程があるのかはじめに全体像を把握しておきましょう。
・sorceryのReset passwordモジュールをインストールする
・Mailerを作成しパスワードリセット用のメールを送信できるようにする
・コントローラとビューの作成
・letter_opener_webを導入しローカル環境でメールが送信されているか確認する
・configを導入し、開発環境と本番環境の設定を分ける
sorceryのサブモジュールをインストール
【モジュールのインストール】
まずはパスワードリセット用のモジュールをインストールします
rails g sorcery:install reset_password --only-submodules
作成されたファイルがこちら。
config/initializers/sorcery.rb
app/models/user.rb
db/migrate/20231105121233_sorcery_reset_password.rb
Rails.application.config.sorcery.submodules = [:reset_password]
class SorceryResetPassword < ActiveRecord::Migration[7.0]
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_column :users, :access_count_to_reset_password_page, :integer, default: 0
add_index :users, :reset_password_token
end
end
カラム | 説明 |
---|---|
reset_password_email_sent_at | はパスワードリセット用のメールを送信した時刻を保持するためのカラム |
reset_password_token_expires_at | トークンの有効期限が切れる時刻を記録するカラム |
reset_password_token | トークン文字列を保持するカラム |
access_count_to_reset_password_page | パスワードリセット画面にアクセスした回数を記録するために用意されたカラムです。N回以上はパスワードリセット画面に移動できないようにするなどの機能を実装する時に用いられるカラム |
作成されたファイルを確認したらコンソールでdb:migrateします。
rails db:migrate
Mailerの作成
Mailerとは、パスワードをリセットする時に自分宛にURL付きのメールが送られて、そのURL先に飛んで新しいパスワードを設定する機能です。
公式Wikiを参考に進めていきます。
【パスワードリセット用のMailerを作成】
rails g mailer UserMailer reset_password_email
作成されたファイル
app/mailers/user_mailer.rb
app/views/user_mailer
app/views/user_mailer/reset_password_email.text.erb
app/views/user_mailer/reset_password_email.html.erb
test/mailers/user_mailer_test.rb
test/mailers/previews/user_mailer_preview.rb
【メールを送信するためのメソッドを作成】
class UserMailer < ApplicationMailer
default from: 'from@example.com'
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
default from: 'from@example.com'
メールの送信元のアドレスを指定できます。
mail(to: user.email,subject: 'パスワードリセット')
メールの宛先、件名を指定します。
@user
と@url
はmailのviewで使用します。
次に、reset_password サブモジュールを追加し、使用するメーラーを定義します。
Rails.application.config.sorcery.submodules = [:reset_password, blabla, blablu, ...]
Rails.application.config.sorcery.configure do |config|
config.user_config do |user|
user.reset_password_mailer = UserMailer
end
end
user.reset_password_mailer =
この記述が405行目にあります(現在)コメントアウトを外し、UserMailerを追加しましょう。
【メールの設定】
<p><%= @user.name %>様</p>
<p>パスワード再発行のご依頼を受け付けました。</p>
<p>こちらのリンクからパスワードの再発行を行ってください。</p>
<p><a href="<%= @url %>"><%= @url %></a></p>
<%= @user.name %>様
パスワード再発行のご依頼を受け付けました。
こちらのリンクからパスワードの再発行を行ってください。
<%= @url %>
Gem sorceryを使い倒す② ログイン機能応用編で、plofileテーブルにnameを作成した場合、<%= @user.profile.name %>様
でnameが獲得できます。
controller作成
コントローラーを作成していきましょう。
rails g controller PasswordResets 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
flash[:success]= 'パスワードリセットのメールを送信しました'
end
def edit
@token = params[:id]
@user = User.load_from_reset_password_token(params[:id])
not_authenticated if @user.blank?
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 login_path
flash[:success]= 'パスワードがリセットされました'
else
render action: 'edit'
end
end
end
公式Wikiに、参考コードが記載されています。コメントアウトで処理の内容も詳しく説明されているのでこちらは省略しています。
ルーティングの設定
resources :password_resets, only: [:create, :edit, :update]
Userモデルにバリデーションを追記
validates :reset_password_token, presence: true, uniqueness: true, allow_nil: true
uniqueness: true
tokenは一意なものでなければならないのでユニーク成約を付けます。これがないと別の人のパスワードを変更できてしまう可能性があります。
allow_nil: true
対象の値がnilの場合にバリデーションをスキップするもの。
トークンの初期値はnilのため、このままではユニーク制約に引っかかりユーザーを複数登録できなくなる問題が発生してしまいます。この問題を回避するために付与します。
viewファイル作成
ログインページに下記のリンクを追加します。
<%= link_to 'passwordをお忘れの方はコチラ', new_password_reset_path %>
パスワード申請フォームと再設定フォームを作成します。
<h3>パスワードリセット申請</h3>
<%= form_with url: password_resets_path do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.submit '申請' %>
<% end %>
<h3>パスワード再設定</h3>
<%= form_with model: @user, url: password_reset_path(@token) do |f| %>
<%= render 'shared/error_message', object: f.object %>
<%= f.label :email %>
<%= @user.email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.submit 'リセット' %>
<% end %>
letter_opener_webの導入
letter_opener_webを使えば、開発環境では実際にメールを送信することなくメールが送信されているか確認することができます。
【letter_opener_webのインストール】
letter_opener_webは開発環境でしか使用しない為、development配下に記述します。
group :development do
gem 'letter_opener_web', '~> 2.0'
end
ターミナルでbundle installします。Dockerを使用している場合はコンテナ内で実行します。
bundle install
【ルーティングの設定】
Rails.application.routes.draw do
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
end
config.action_mailer.delivery_method = :letter_opener_web # 送信方法を指定
config.action_mailer.perform_deliveries = true # メールを実際に送信するかどうかを指定
こちらのアドレスでletter_opener_webを開けます。
http://localhost:3000/letter_opener
gem configを導入する
【gemのインストール】
configは定数を管理するgemです。configを導入することで、ローカル環境でのメールをletter_opener_web、本番環境のメールを指定したプロトコルでなど、簡単に分けることが可能です。
gem 'config'
Gemfileに記述したらbundle install
します。
【ファイルを生成】
rails g config:install
生成されるファイル
config/initializers/config.rb
config/settings.yml
config/settings.local.yml
config/settings
config/settings/development.yml
config/settings/production.yml
config/settings/test.yml
.gitignore
ファイル名 | 役割 |
---|---|
config/initializers/config.rb | configの設定ファイル |
config/settings.yml | すべての環境で利用する定数を定義 |
config/settings.local.yml | ローカル環境のみで利用する定数を定義 |
config/settings/development.yml | 開発環境のみで利用する定数を定義 |
config/settings/production.yml | 本番環境のみで利用する定数を定義 |
config/settings/test.yml | テスト環境のみで利用する定数を定義 |
.gitignoreには以下の内容が自動で追記されます。
config/settings.local.yml
config/settings/*.local.yml
config/environments/*.local.yml
【開発環境での定数設定】
config.action_mailer.delivery_method = :letter_opener_web
config.action_mailer.default_url_options = Settings.default_url_options.to_h
42行目辺りにaction_mailer
のコードがあるので、その下辺りに記述すると分かりやすいです。
config.action_mailer.delivery_method = :letter_opener_web
Railsアプリケーションでメールを送信する際に使用する配送方法(delivery method)を設定しています。
config.action_mailer.default_url_options = Settings.default_url_options.to_h
ActionMailerがメールを生成する際に使用するデフォルトのURLオプションを設定しています。
以下のdevelopment.ymlで記述された定数default_url_options
を読み込んでいます。
default_url_options:
host: 'localhost:3000'
開発環境でのhost設定です。
【本番環境での定数設定】
config.action_mailer.default_url_options = Settings.default_url_options.to_h
default_url_options:
protcol: 'https'
host: 'example.com'
本番環境は別回で詳しく書きます。
参考資料
関連記事