実装の流れ
- sorceryのReset passwordモジュールをインストールする
- Mailerの作成
- コントローラの作成
- トークンにユニーク制約を付与
- ビューファイルの作成
- letter_opener_webの導入
- configを導入
公式wikiに手順が説明されている。
https://github.com/Sorcery/sorcery/wiki/Reset-password
sorceryのサブモジュールをインストール
$ rails g sorcery:install reset_password --only-submodules
以下のファイルが生成される。
config/initializers/sorcery.rb
app/models/user.rb
db/migrate/20231105121233_sorcery_reset_password.rb
しかし私の場合、app/models/user.rbでエラーのような文が表示された。
gsub config/initializers/sorcery.rb
File unchanged! The supplied flag value not found! app/models/user.rb
create db/migrate/20240227224617_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回以上はパスワードリセット画面に移動できないようにするなどの機能を実装する時に用いられるカラム |
マイグレーションする
$ rails db:migrate
パスワードリセット用のMailerを作成する
rails g mailer UserMailer reset_password_email
生成されたファイルを確認
create app/mailers/user_mailer.rb
invoke erb
create app/views/user_mailer
create app/views/user_mailer/reset_password_email.text.erb
create app/views/user_mailer/reset_password_email.html.erb
app/mailers/user_mailer.rbが作成されている。
app/views/user_mailerの下に二つのビューファイルが作成されている。
html形式のメールとtext形式のメールが用意されているということ。
メールを送信するメソッドを作成
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
@userと@urlはmailのviewファイルで使用する。
パスワードリセットに使用するmailerを定義する
config/initializers/sorcery.rb にreset_passwordサブモジュールを追加し、パスワードリセットに使用するActionMailerとしてUserMailerを定義する記述をする。
Rails.application.config.sorcery.submodules = [:reset_password]
Rails.application.config.sorcery.configure do |config|
config.user_config do |user|
user.reset_password_mailer = UserMailer
end
end
既にファイル内に記述がコメントアウトしてあったので、必要分のみ追記しました。
user.reset_password_mailer = の部分にUserMailerを付け加え
Mailerのビューファイルの設定
<%= @user.decorate.full_name %>様
===============================================
パスワード再発行のご依頼を受け付けました。
こちらのリンクからパスワードの再発行を行ってください。
<%= @url %>
<p><%= user.decorate.full_name %>様</p>
<p>パスワード再発行のご依頼を受け付けました。</p>
<p>こちらのリンクからパスワードの再発行を行ってください。</p>
<p><a href="<%= @url %>"><%= @url %></a></p>
@url の中身は、tokenで発行された再登録用のurlのこと。
コントローラの作成
rails g controller PasswordResets create edit update
create app/controllers/password_resets_controller.rb
invoke erb
create app/views/password_resets
create app/views/password_resets/create.html.erb
create app/views/password_resets/edit.html.erb
create app/views/password_resets/update.html.erb
invoke decorator
create app/decorators/password_reset_decorator.rb
コントローラの中身を記述
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: t('.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: t('.success')
else
flash.now[:danger] = t('.fail')
render :edit, status: :unprocessable_entity
end
end
end
ルーティングの設定
resources :password_resets, only: [:new, :create, :edit, :update]
usersテーブルのtokenにunique制約とuniqueバリデーションを付与
tokenは一意なものでなければならないのでユニーク制約を付ける。
validates :reset_password_token, uniqueness: true, allow_nil: true
ビューファイルを編集
ログインページにパスワードリセットページへのリンクを追加
<%= link_to t('user_sessions.new.to_register_page'), new_user_path %>
<%= link_to t('user_sessions.new.password_forget'), new_password_reset_path %> #追記
パスワードリセット申請画面を作成
<% content_for(:title, t('.title')) %>
<div class="container">
<div class="row">
<div class="col-md-10 col-lg-8 mx-auto">
<h1><%= t('.title') %></h1>
<%= form_with url: password_resets_path do |f| %>
<div class="mb-3">
<%= f.label :email, class: "form-label" %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<%= f.submit t('.submit'), class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
パスワードリセット画面を作成
<% content_for(:title, t('.title')) %>
<div class="container">
<div class="row">
<div class="col-md-10 col-lg-8 mx-auto">
<h1><%= t('.title') %></h1>
<%= form_with model: @user, url: password_reset_path(@token), method: :patch do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="mb-3">
<%= f.label :email, class: "form-label" %>
<%= @user.email %>
</div>
<div class="mb-3">
<%= f.label :password, class: "form-label" %>
<%= f.password_field :password, class: "form-control" %>
</div>
<div class="mb-3">
<%= f.label :password_confirmation, class: "form-label" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
<%= f.submit class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
letter_opener_webを追加する
letter_opener_weを使うと、開発環境で実際にメールを送信せずに、メールの送信を確認できる。
https://github.com/fgrehm/letter_opener_web
開発環境でしか使わないため、development配下に記載する。
group :development do
gem 'letter_opener_web', '~> 2.0'
end
bundle installする
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
config.action_mailer.default_url_options = { host: 'localhost:3000' }
メールが届くかどうかは以下の画面から確認する
configを導入する
gem configを利用して、環境ごとの定数を便利に管理できるようになる。
gem 'config'
bundle install
以下のコマンドを入力してファイルを生成
rails g config:install
ファイルが生成される
create config/initializers/config.rb
create config/settings.yml
create config/settings.local.yml
create config/settings
create config/settings/development.yml
create config/settings/production.yml
create config/settings/test.yml
append .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
config.action_mailer.default_url_options = Settings.default_url_options.to_h
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を読み込んでいる。
host情報をsettings/development.ymlに記載する
ホスト情報は開発環境とテスト環境の両方に設定する
default_url_options:
host: 'localhost:3000'
default_url_options:
host: 'localhost:3000'