sorceryのreset_passwordモジュールを使用したパスワードリセット機能

どうもどハマりエンジニアです。

 

本日はパスワードのリセット機能について実装していきます。

使う概念

  • soceryのreset_passwordモジュール
  • letter_opener_web
  • config

sorceryのreset_passwowrdモジュールをインストールする


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

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_column :users, :access_count_to_reset_password_page, :integer, default: 0

    add_index :users, :reset_password_token
  end
end

そうすると上記のmigrationが作成されます。

rails db:migrate 実行

Mailerの作成

$rails g mailer UserMailer reset_password_email

(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: 'パスワードリセット')
  end
end
  • default from: 'from@example.com'でメールの送信元のアドレスを指定できる
  • mail(to: user.email,subject: 'パスワードリセット')でメールの宛先、件名を指定

 

(sorcery.rb)

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

  config.user_class = 'User'
end

sorcery.rbにreset_passwordサブモジュールを追加し、パスワードリセットに使用するActionMailerとしてUserMailerを定義

 

 

コントローラー・ビューの作成

$rails g controller password_resets

(password_resets_controller.rb)

class PasswordResetsController < ApplicationController
  skip_before_action :require_login
  def new; end

  def create
    @user = User.find_by(email: params[:email])
    if @user
      @user.deliver_reset_password_instructions!
    redirect_to login_path, success: 'パスワードリセット手順を送信しました'  
    else
    render 'new'
    end
  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(@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

 

create()では送信されてきたメールに対応するUserを検索し、「deliver_reset_password_instructions!」を呼び出すことでトークンの発行とメール送信を行っています。

edit()はトークンの存在チェックです。

update()では「change_password!」を呼び出してパスワードの変更を行っています。APIであるため、それぞれのアクションではHTTPステータスコードを状態に応じて返しています。

 

(routes.rb) ルーティングの設定

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

 

(new.html.erb)リセット申請画面

<%= form_with url: password_resets_path, local: true, method: :post do |f| %>
  <div class="field">
    <%= f.label :email, t(User.human_attribute_name(:email)) %><br />
    <%= f.text_field :email, class: 'form-control' %>
    <%= f.submit '送信', class:'btn btn-primary' %>
  </div>
<% end %>

 

(edit.html.erb)リセット編集画面

<%= form_with model: @user, url: password_reset_path(@token), method: :patch, local: true do |f| %>
<div class="field">
  <%= f.label :email %>
  <%= @user.email %>
</div>
<div class="field">
  <%= f.label :password %>
  <%= f.password_field :password, class: 'form-control' %>
</div>
<div class="field">
  <%= f.label :password_confirmation %>
  <%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div class="actions">
  <%= f.submit '更新する', class: 'btn btn-primary' %>
</div>
<% end %>

 

メール文作成

(reset_password_email.html.erb)

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

@urlは先ほどmailer.rbで作成

 

(reset_password_email.text.erb)

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

 

letter_opener_webを追加し、開発環境では実際のメールは送られないように設定

下記のgemをインストールする
'letter_opener_web'

(routes.rb)設定を加える
mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?

 

configのgemを追加

rails g config:install
config/settings.yml
config/settings.local.yml
config/settings/development.yml
config/settings/production.yml
config/settings/test.yml

上記のファイルが作成されるので、各ファイルに必要な記述をしていきます。

 

(config/environments/development.rb)

config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: Settings.host }
config.action_mailer.delivery_method = :letter_opener_web

 

(settings/development.yml)

host: 'localhost:3000'

 

上記でパスワードリセットの実装は完了です。

実際にパスワードのリセットを申請すると、http://localhost:3000/letter_opener にメールが届くので確認してください。

 

以上で本日の実装は終了です。