はじめに
アプリ制作の課題に取り組んでいる中で、改めて過去に行った課題を参考にしていたら流れが理解できていない状態。改めて自分が何をしているのか、コードの意味を含め一度整理しておくためにもリセットパスワードの全体の流れをアウトプットしたいと思います。
環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3
1.gem 'letter_opener_web'の設定( http://localhost:3000/letter_opener)
-
Gemfileに追記する(開発環境!)
group :development do gem 'letter_opener_web', '~> 3.0' end
-
コンテナ内に入りインストールを行う
$ docker exec コンテナ名 bash # bundle install インストール後はサーバーを立ち上げなおす(この過程を省くと変なエラーが起こる可能性あり) $ docker compose restart Dockerコンテナ内で開発環境も起動しておく $ docker compose exec web bin/dev
-
config/routes.rbに追記
・開発環境 (Rails.env.development? が true の場合) において、/letter_opener というURLで Letter Opener Web のインターフェースにアクセスできるようになります。これにより、送信されたメールの内容をブラウザ上で確認できます。Your::Application.routes.draw do mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? end
-
config/environments/development.rb の設定
・1つめはメールの配信方法 (delivery_method) を letter_opener_web に指定しています。
・2つ目については RailsのActionMailer
では、メール内に含めるURLを生成する際にdefault_url_options
で指定されたホスト名とポート番号が使用されますが、この設定がないことでリンク生成が失敗してしまいます。また、パスワードリセットのメールでedit_password_reset_url(@user.reset_token)
のようにURLを生成する場合、ホスト名が設定されていないとリンクが正しく構築されません。その結果、ユーザーがリンクをクリックしても期待するページにアクセスできず、エラーが発生する可能性があります。# letter_opener_web の設定 config.action_mailer.delivery_method = :letter_opener_web # メーラーでのホスト情報 config.action_mailer.default_url_options = { host: 'localhost:3000' }
-
letters_location の設定について
・公式にはconfig/environments/development.rbに次の設定をすることも提案されていましたが、開発環境はデフォルトを使用すればいいのでこちらの設定はしませんでした。このコードを追加することで、手紙の保存場所を変更できますLetterOpenerWeb.configure do |config| config.letters_location = Rails.root.join('your', 'new', 'path') end
2.パスワードリセット
-
Sorcery のサブモジュール、reset_passwordの導入
・ユーザーがパスワードを忘れた場合に、再設定するための手続きを提供する機能を実装。
・rails g sorcery
だとSorceryの全ての標準機能をインストールしてしまうが:install reset_password --only-submodules
を加えることでreset_passwordに必要なファイルだけが生成される。$ docker exec コンテナ名 bash # rails g sorcery:install reset_password --only-submodules
-
生成されたマイグレーションファイルに追記する(reset_password_tokenカラム)
・changeメソッドの中に書かれた内容が、データベースに対して行われる変更を定義し、メソッド内のコードが実行されると、データベースが更新される。
・add_columnメソッドを使って、usersテーブルに新しいカラムを追加
・それぞれのカラムは上から順に、「パスワードリセット用のトークン・トークンの有効期限・パスワードリセット用のメールが送信された日時・パスワードリセットページにアクセスした回数」を保存するためのカラム
・reset_password_token
カラムにインデックスを追加する事で、トークン1の検索が速くなり、unique: true
と指定してるから、同じトークンを持つレコードが2つ存在できないように制約がかかる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_column :users, :access_count_to_reset_password_page, :integer, default: 0 add_index :users, :reset_password_token, unique: true end end
-
マイグレーションファイルに追記後以下実行
# rails db:migrate
3.メールアクションを1つ持つメイラーを追加
- Railsのメール機能をサポートするためのファイルを生成する
# rails g mailer UserMailer reset_password_email
- app/mailers/user_mailer.rbファイルに
user
パラメータを追加他
・user は、メールを送信する対象となるユーザーを指します。
・@user というインスタンス変数を作成し、user.id を使ってデータベースからそのユーザーを取得
・@url というインスタンス変数に、パスワードリセットページへのリンクを生成し、そのURLを代入
・mail メソッドは、メールを送信するために使用されます。to: user.email でメールの送信先を指定し、subject: "件名" でメールの件名を設定class UserMailer < ApplicationMailer default from: 'no-reply@アプリ名.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
4.reset_password
サブモジュールを追加し、使用するメイラーを定義
- Sorceryの初期設定ファイルの編集(config/initializers/sorcery.rbファイル)
・UserMailer を使用してリセットメールを送信するように構成していく。
・コメントアウトを外して定義していく。
・config
という設定オブジェクトを使いSorcery
の動作をカスタマイズしていく
・user_config
ブロック内では、個々のユーザーに対する認証の動作をカスタマイズ。ここで設定された内容は、アプリケーションの全ユーザーに適用‼
・stretches
は、パスワードのハッシュ化2の回数を指定
・authentications_class
を使うことで、ユーザーの認証に関する処理を柔軟に管理できるようになるが不要なら削除。
※外部認証機能を使わない場合、external
は外してください。blablu
以降はご自身が使用する機能など。Rails.application.config.sorcery.submodules = [:reset_password, :external, blablu, ...] Rails.application.config.sorcery.configure do |config| config.user_config do |user| user.stretches = 1 if Rails.env.test? user.reset_password_mailer = UserMailer user.authentications_class = Authentication end end
5.Userモデルにバリテーション追加
・validates
はRailsのバリデーションメソッドで、モデルの属性に対して様々な条件を設定するために使う
・uniqueness: true
によって同じトークンが存在する場合トークンの再利用を防ぐ
・allow_nil: true
により、リセットパスワードトークンがnil
の場合エラー発生を防ぐ。
validates :reset_password_token, uniqueness: true, allow_nil: true
6.パスワードアクションを処理するコントローラの作成
-
コンテナ内で下記コマンド実行
# rails g controller PasswordResets create edit update
-
app/controllers/password_resets_controller.rb
編集class PasswordResetsController < ApplicationController skip_before_action :require_login def create @user = User.find_by_email(params[:email]) @user.deliver_reset_password_instructions! if @user redirect_to(root_path, :notice => 'Instructions have been sent to your email.') end def edit @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end 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(root_path, :notice => 'Password was successfully updated.') else render :action => "edit" end end end
※上記コードは資料参考のものだが次の通りパラメータを用いたりエラー文を表示するよう変更も可能
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, notice: 'メールを送信しました' else flash.now[:alert] = 'メールアドレスが見つかりません' render :new, status: :unprocessable_entity end end # パスワードリセットフォーム def edit @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) # ユーザーがいなければログインページへ遷移 if @user.blank? not_authenticated return end end def update @token = params[:id] # params に含まれるトークンからユーザを特定 @user = User.load_from_reset_password_token(params[:id]) # ユーザが見つからない場合 if @user.blank? # ログイン画面に遷移 not_authenticated return end # バリデーションチェック @user.password_confirmation = password_params[:password_confirmation] # トークンをクリアして、パスワードを更新 if @user.change_password(password_params[:password]) # ログインページに遷移し、フラッシュメッセージを表示 redirect_to login_path, notice: 'パスワードを再設定しました' else # 再設定画面を再表示 flash.now[:alert] = '再設定に失敗しました' render :edit, status: :unprocessable_entity end end def password_params params.require(:user).permit(:password, :password_confirmation) end end
7.残りを追加
-
ルーティングの設定
・password_resets
コントローラーの各アクション等に対するルート設定をすることで、パスワードリセットのリクエストやフォーム表示、リセット処理が行えるようになる。# パスワードリセット resources :password_resets, only: %i[new create edit update]
-
app/views/layouts/mailer.html.erbファイルについて
・資料には= yield
と記載されいていますが。特に修正は不要かと。 -
app/views/user_mailer/reset_password_email.text.erb設定
・パスワードリセットのメールテンプレートです。ユーザーのメールアドレスとリセット用のリンクが含まれています。<%= @user.name %>様 =============================================== パスワード再発行のご依頼を受け付けました。 こちらのリンクからパスワードの再発行を行ってください。 <%= @url %>.
-
app/views/user_mailer/reset_password_email.html.erb設定
・Rails の Action Mailer では、通常、両方のテンプレートを用意し、受信者のメールクライアントが対応している形式でメールを送信します。なので今回はこちらもコードを入力しておきます。<p><%= @user.name %>様</p> <p>===============================================</p> <p>パスワード再発行のご依頼を受け付けました。</p> <p>こちらのリンクからパスワードの再発行を行ってください。</p> <p><a href="<%= @url %>"><%= @url %></a></p>
8.パスワードリセット用ビューの設定(editとnewに関しては資料をもとに自身が好きなフォームにアレンジ)
-
app/views/password_resets/edit.html.erb
・ユーザーが新しいパスワードを入力するフォームです。トークンを使ってリセットリンクからユーザーを特定し、新しいパスワードを設定します。 -
app/views/user_sessions/new.html.erb
・ログインページに「パスワードを忘れた?」リンクを追加<%= link_to 'Forgot Password?', new_password_reset_path %>
-
app/views/password_resets/new.html.erb
・「新しい」ビューを作成し、ログインページからリンクを設定
9.gem 'config'の設定
-
gem 'config'とは、Railsアプリケーションで設定管理を簡単に行うためのgemです。
-
環境ごとに異なる設定を一元管理することができ、開発環境、テスト環境、本番環境で異なる設定値を使いたい場合に非常に便利です。
Gemfileに追記 gem 'config' コンテナ内で以下コマンド実行してインストールしサーバー立ち上げなおす $ bundle install # docker compose restart # docker compose exec web bin/dev 設定の初期化を行う為つぎのコマンドを実行する $ rails g config:install
-
config/settings/development.yml例えばこんな感じで設定
default_url_options: host: 'localhost:3000'
-
上記ファイルで定義した値を呼び出すようにconfig/environments/development.rbを編集
・to_hは、オブジェクトをハッシュに変換するメソッドで、config.action_mailer.default_url_options
がハッシュ形式のオプションを期待しているからつけないとエラーになってしまう。config.action_mailer.default_url_options = Settings.default_url_options.to_h
※config/配下を編集した場合は各種サーバーを立ち上げ直してください
- .gitignoreを編集する
・特定のファイルやディレクトリをGitの管理から除外するため
・config/settings.local.ymlファイルなどには、データベースのパスワードやAPIキーなどの機密情報が含まれることが多いから、これらをGitに含めないようにする必要がある
・また、.local.ymlというファイル名は、開発環境やテスト環境専用の設定を含むことが一般的。これらは開発者ごとに異なるため、リポジトリに含めると混乱を招くことがあるconfig/settings.local.yml config/settings/*.local.yml config/environments/*.local.yml
さいごに
ここまでごらんいただきありがとうございます。今回の記事が何か参考になればうれしい限りです。
また、誤字脱字など気づいた点等ございましたら、ご指摘いただけると幸いです。