29
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gem sorceryを使い倒す③ パスワードリセット機能

Last updated at Posted at 2023-11-07

パスワードリセット実装までの流れ

 
パスワードリセットの工程は長いので、どんな工程があるのかはじめに全体像を把握しておきましょう。

・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

config/initializers/sorcery.rb
Rails.application.config.sorcery.submodules = [:reset_password]
migrate/20231105121233_sorcery_reset_password.rb
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

 
 
【メールを送信するためのメソッドを作成】

app/mailers/user_mailer.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 サブモジュールを追加し、使用するメーラーを定義します。

config/initializers/sorcery.rb
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を追加しましょう。

 
【メールの設定】

app/views/user_mailer/reset_password_email.html.erb
<p><%= @user.name %>様</p>

<p>パスワード再発行のご依頼を受け付けました。</p>

<p>こちらのリンクからパスワードの再発行を行ってください。</p>

<p><a href="<%= @url %>"><%= @url %></a></p>
app/views/user_mailer/reset_password_email.text.erb
<%= @user.name %>様

パスワード再発行のご依頼を受け付けました。

こちらのリンクからパスワードの再発行を行ってください。

<%= @url %>

Gem sorceryを使い倒す② ログイン機能応用編で、plofileテーブルにnameを作成した場合、<%= @user.profile.name %>様でnameが獲得できます。

controller作成

 
 
コントローラーを作成していきましょう。

rails g controller PasswordResets create edit update
app/controllers/password_resets_controller.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
    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に、参考コードが記載されています。コメントアウトで処理の内容も詳しく説明されているのでこちらは省略しています。

 
ルーティングの設定

config/routes.rb
resources :password_resets, only: [:create, :edit, :update]

 
Userモデルにバリデーションを追記

models/user.rb
validates :reset_password_token, presence: true, uniqueness: true, allow_nil: true

uniqueness: true
tokenは一意なものでなければならないのでユニーク成約を付けます。これがないと別の人のパスワードを変更できてしまう可能性があります。

allow_nil: true
対象の値がnilの場合にバリデーションをスキップするもの。
トークンの初期値はnilのため、このままではユニーク制約に引っかかりユーザーを複数登録できなくなる問題が発生してしまいます。この問題を回避するために付与します。

viewファイル作成

 
 
ログインページに下記のリンクを追加します。

app/views/user_sessions/new.html.erb
<%= link_to 'passwordをお忘れの方はコチラ', new_password_reset_path %>

 
パスワード申請フォームと再設定フォームを作成します。

password_resets/new.html.erb
<h3>パスワードリセット申請</h3>
<%= form_with url: password_resets_path do |f| %>
    <%= f.label :email %>
    <%= f.email_field :email %>
    <%= f.submit '申請' %>
<% end %>
password_resets/edit.html.erb
<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配下に記述します。

Gemfile
group :development do
  gem 'letter_opener_web', '~> 2.0'
end

ターミナルでbundle installします。Dockerを使用している場合はコンテナ内で実行します。

bundle install

【ルーティングの設定】

routes.rb
Rails.application.routes.draw do
  mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?

end
config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener_web # 送信方法を指定
config.action_mailer.perform_deliveries = true # メールを実際に送信するかどうかを指定

【参考】action_mailer様々な設定方法

  
こちらのアドレスでletter_opener_webを開けます。
http://localhost:3000/letter_opener

gem configを導入する

  
【gemのインストール】
configは定数を管理するgemです。configを導入することで、ローカル環境でのメールをletter_opener_web、本番環境のメールを指定したプロトコルでなど、簡単に分けることが可能です。
  

Gemfile
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には以下の内容が自動で追記されます。

.gitignore
config/settings.local.yml
config/settings/*.local.yml
config/environments/*.local.yml

  
【開発環境での定数設定】

config/environments/development.rb
  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を読み込んでいます。
  

config/settings/development.yml
default_url_options:
  host: 'localhost:3000'

開発環境でのhost設定です。

  
【本番環境での定数設定】

config/environments/production.rb
config.action_mailer.default_url_options = Settings.default_url_options.to_h
config/settings/production.yml
default_url_options:
  protcol: 'https'
  host: 'example.com'

本番環境は別回で詳しく書きます。
  

参考資料

関連記事

29
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?