しおブログ言うてますけど

技術ブログ言うてますけど技術以外のことも発信すると思われ。RubyとかRuby on Railsとか生き方とかジャンルにこだわらず自分の好きなように。初心者なのでお手柔らかに。

sorceryでパスワードリセット機能を実装する

こんにちは。しおはまです。
今回はsorceryというGemを使ってパスワードをリセットする機能を実装する過程をまとめてみたいと思います。

sorceryとは

そもそもsorceryってどういうGemなのか説明します。
Sorceryは、ユーザ認証機能を簡単に実装できるライブラリで、MITライセンスのオープンソースソフトウェアとして公開されています。

github.com

↑がsorcerygithubです。
ユーザの認証の基本的な機能であるパスワード認証を始め、

  • User Activation
  • Reset Password
  • Remember Me
  • Session Timeout
  • Brute Force Protection
  • Basic HTTP Authentication
  • Activity Logging

といった機能が揃っており、必要に応じて機能を選んで使うことができます。
ログイン機能やパスワード認証の実装についても今後ブログでまとめたいですが、今回はパスワードをリセットする方法にフォーカスを当ててまとめていきたいと思います。
なお、ログイン機能については今回は実装できている前提で説明をしますのでご了承ください。

sorceryのインストール

さっそくパスワードをリセットする方法について見ていきます。
まずは、Gemfilesorceryを記述してbundle installしましょう。

 # Gemfile

gem 'sorcery'

パスワードのリセットを実装するにはsorceryReset Passwordというmoduleを使用します。

github.com

こちらのgithubに一連の流れは書いてあるので、より正しい情報が知りたいという方は参照してみてください。

moduleのインストールとマイグレーションファイルの作成

まず初めの手段として下記のコマンドでmoduleのインストールとマイグレーションファイルの作成をします。

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

コマンドを叩くと、自動的にマイグレーションファイルが作成されます。

class SorceryResetPassword < ActiveRecord::Migration
  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_index :users, :reset_password_token
  end
end

マイグレーションファイルが作成されたことを確認して、マイグレーションを実行します。

$ rails db:migrate

config/initializers/sorcery.rb(sorceryの設定ファイル)を見ると自動的にpassword_resetのサブモジュールを使用することが書かれています。

# Gemfile

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

PasswordResetsコントローラーの追加

ここまで設定できたらrails gコマンドでPasswordResetsコントローラーを作成します。
アクションはパスワードを変更するcreate、編集ファイルのedit、作成するためのupdateを指定します。

rails g controller PasswordResets create edit update

PasswordResetsコントローラーが作成できたらReset passwordのgithubにしたがってコードを記述します。(多少カスタマイズしているところもありますが、おおまかなところは変わり無いです)

class PasswordResetsController < ApplicationController
  skip_before_action :require_login

  def create
    @user = User.find_by(email: params[:email])
    @user&.deliver_reset_password_instructions! if @user
    flash[:success] = 'パスワードリセット手順を送信しました'
    redirect_to root_path
  end

  def edit
    @token = params[:id]
    @user = User.load_from_reset_password_token(params[:id])
    return 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])
      flash[:success] = 'パスワードを変更しました'
      redirect_to login_path
    else
      flash.now[:danger] = 'パスワードを変更できませんでした'
      render :edit
    end
  end
end

細かいメソッドの説明は割愛しますが、createアクションはパスワードをリセットするためのメールアドレスを送信し、どのユーザーのメールアドレスかを判定します。
editアクションでは実際にパスワードをリセットするための判定がされています。(詳しくはgithubWikiload_from_reset_password_tokenの動きなどを確認するといいと思います)。
updateアクションではパスワードをリセットする動きを制御しています。

 if @user.change_password

の部分でパスワードの条件分岐をしており、正しくパスワードが入力されたらtrueになり、正常にパスワードがリセットされるということになります。

ルーティングの設定

コントローラーまで設定できたら次にルーティングの設定をしていきます。
ルーティングの設定は簡単ですね。

# route.rb

resources :password_resets, only: [:create, :edit, :update]

これでルーティングが正しく設定されますね。

送信メールの設定

ルーティングの設定ができたら送信するためのメールを設定していきます。

# reset_password_email.text.erb

Hello, <%= @user.email %>
===============================================

You have requested to reset your password.

To choose a new password, just follow this link: <%= @url %>

Have a great day!

ここは自分が送信メールで設定できる部分なので自由にコードを書いてもいいのですが、<%= @user.email %>でパスワードをリセットするために送信したメールアドレス、<%= @url %>にパスワードをリセットするためのURLが記載されますので、ご注意ください。

# app/mailers/user_mailer.rb

def reset_password_email(user)
  @user = User.find user.id
  @url  = edit_password_reset_url(@user.reset_password_token)
  mail(:to => user.email,
       :subject => "Your password has been reset")
end

user_mailer.rbApplicationMailerを継承しており、メールに関する設定を変更できます。
特にmail(to: user.email, subject: 'パスワードリセット')で、メールの送信元、メールの題名を設定することができます。

フォームの作成

ここまで設定できたらあとはフォームを作成するだけです。
どのようにフォームを作成するかは個人個人で違うと思いますが、githubを参考に# app/views/password_resets/new.html.erb# app/views/password_resets/create.html.erbにコードを書いていくと簡単に作成することができます。

まとめ

ここまで読んでいただきありがとうございます。
長くなってしまいましたが、パスワードリセット機能はログイン機能を実装したら必然的にリセット機能も必要になってくるのでしっかり理解しておきたいところですね。
にしてもsorceryは使いたい機能を使う時にサブモジュールをインストールして自分で実装できるのがいいですね。