【Rails】sorcery(Reset password)
はじめに
sorceryとは、Railsに認証機能の実装を行うためのライブラリです。 同じように認証機能を提供してくれているものとしてdeviseなどが挙げられますが、sorceryの方がよりシンプルで、カスタマイズ性に富んでいるという特徴を持ちます。今回はsorceryのReset Passwordモジュールの実装方法を記していきます。
実装方法
モジュールのインストール
$ rails g sorcery:install reset_password --only-submodules Running via Spring preloader in process 30110 gsub config/initializers/sorcery.rb insert app/models/user.rb create db/migrate/20200419075054_sorcery_reset_password.rb
これで、password_resetサブモジュールを使用するための記述がconfig/initializers/sorcery.rbに、自動で行われました。また、マイグレーションファイルなども作成されます。
マイグレーションファイル
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
この、マイグレーションファイルをDBに反映させます。
$ rails db:migrate
パスワードリセット用のMailerを作成
まず、Mailerを作成します。
$ rails g mailer UserMailer reset_password_email Running via Spring preloader in process 88211 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
次に、config/initializers/sorcery.rbの中で、sorceryのパスワードリセットに使用するActionMailerをUserMailerで指定します。
config/initializers/sorcery.rb
Rails.application.config.sorcery.submodules = [:reset_password] Rails.application.config.sorcery.configure do |config| config.user_config do |user| # パスワードリセット用のMailerにUserMailerを指定する user.reset_password_mailer = UserMailer end end
それでは、パスワードリセット用のメソッドを記述します。
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 # reset_password_emailメソッドなので、reset_password_email.〇〇のビューがメールのフォーマットになる # メイラーのメソッド内で定義されたインスタンス変数はメイラーのビューで使える。
最後に、メイラービューの設定をします。
app/views/user_mailer/reset_password_email.html.erb
<h1><%= @user.decorate.full_name %>様</h1> <p>=====================================</p> <p>パスワード再発行のご依頼を受け付けました。</p><br> <p>こちらのリンクからパスワードの再発行を行ってください。</p> <p><a href="<%= @url %>"><%= @url %></a></p>
app/views/user_mailer/reset_password_email.text.erb
<%= @user.decorate.full_name %>様 =========================================== パスワード再発行のご依頼を受け付けました。 こちらのリンクからパスワードの再発行を行ってください。 <%= @url %>
リセットをするためのフォームからコントローラまで
routes.rb
resources :password_resets, only: %i[new create edit update]
まずは、パスワードリセットのトークンを発行しメールを送るためのフォーム。
passowrd_reserts/new.html.erb
<% content_for(:title, t('.title')) %> <div class="container"> <div class="row"> <div class=" col-md-10 offset-md-1 col-lg-8 offset-lg-2"> <h1><%= t('.title') %></h1> <%= form_with url: password_resets_path, method: :post, local: true do |f| %> <div class="form-group"> <%= f.label :email, User.human_attribute_name(:email) %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="actions"> <%= f.submit t('default.sent'), class: 'btn btn-primary' %> </div> <% end %> </div> </div> </div>
email情報からパスワードをリセットしたい、userを抜き出す。
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]) # ここで先ほど定義したメソッドのapp/mailers/user_mailer.rbへ @user&.deliver_reset_password_instructions! redirect_to login_path, 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]) 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 action: 'edit' end end end
createメソッドが動き、さきほどの設定したメールテンプレートが送信されます。そのトークンが含まれたURLからeditアクションを起動しapp/views/password_resets/edit.html.erbへ遷移されます。
app/views/password_resets/edit.html.erb
<% content_for(:title, t('.title')) %> <div class="container"> <div class="row"> <div class=" col-md-10 offset-md-1 col-lg-8 offset-lg-2"> <h1><%= t('.title') %></h1> <%= form_for @user, :url => password_reset_path(@token), :html => {:method => :put} do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="form-group"> <%= f.label :email %><br /> <%= @user.email %> </div> <div class="form-group"> <%= f.label :password %><br /> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> <div class="actions"> <%= f.submit class: 'btn btn-primary' %> </div> <% end %> </div> </div> </div>
このフォームでボタンを押すと、updateアクションが起動し、パスワードリセット完了です。