シンプルな組み込みの認証機能(2)
認証機能の利用例
具体的に認証機能を試してみます。サインアップ機能がないので、db/seeds.rbファイルにユーザーを1件のみ登録し、マイグレーションがまだならrails db:migrateコマンドを、そのあとにrails db:seedコマンドを実行します。Userモデルにはhas_secure_passwordメソッドが記述されているので、平文のパスワードを引数passwordで渡せば自動的にハッシュをpassword_digest属性に格納してくれます(第1回を参照)。
なお、後でパスワードリセットを試す場合には、ここに記載するメールアドレスは実際に受信可能なものに変更するか、追加する必要があります。また、メール送信に使うAction Mailerをメール送信できるように構成する必要もあります。
User.create(email_address: 'user@example.com', password: 'password')
[NOTE]Action Mailerの構成
配布サンプルでは、Action Mailerは既定値のままとなっているので、メール送信は行えません。app/mailers/application_mailer.rbファイルのdefaultメソッドに有効な送信元メールアドレスを指定し(既定はfrom@example.com)、config/environments/development.rbファイルに、例えば以下の設定を追加してください。SMTPサーバなどはご自身の環境に合わせてください。
config.action_mailer.raise_delivery_errors = true config.action_mailer.smtp_settings = { address: "SMTPサーバ", port: ポート番号, domain: "HELOドメイン" }
テスト用のページを、例えばauth#indexなどとして作成します。
% rails generate controller auth index
indexのビューに、サインインしているユーザーのメールアドレスやサインアウト、サインインのリンクなどを追記します。
<% if authenticated? %> <p><%= Current.user.email_address %> としてサインインしています。</p> <%= link_to 'サインアウト', session_path, data: { turbo_method: :delete } %> <% else %> <p>サインインしていません。</p> <%= link_to 'サインイン', new_session_path %> <% end %>
サインアウト時にはトップページにリダイレクトされるので、ルート定義ファイルのrootメソッドを以下のように有効にしておきます。
root "auth#index"
アプリケーションをrails serverコマンドで起動し、ブラウザからトップページにアクセスします。サインインしていないので、Sessionコントローラーのnewアクションが呼び出されて、サインインページが表示されます。
初期データとして登録したメールアドレスとパスワードでサインインすると、authenticated?メソッドによってサインインしているとして、メールアドレスとサインアウトのリンクが表示されます。
サインアウトすると、再びサインインページになります。ここで「Forgot password?」リンクをクリックすると、Passwordコントローラーのnewアクションによりパスワードリセット申請フォームが表示されるので、メールアドレスを入力してボタンを押すとパスワードリセットメールが送信されます。
なお、ここで入力するメールアドレスは、usersテーブルに登録されている(サインアップされている)もので、かつ受信が可能である必要があります。
以下のメール(Subject: Reset your password)が到着します。テキストパート、HTMLパートともにリンクが1個設置されています。リンクの有効期限は15分となっています。
リンクをクリックすると、パスワードリセットフォームが開くので、新しいパスワードを入力して[Save]ボタンをクリックすれば、変更が完了します。
Sessionsコントローラーを見てみる
最後に、実装を少し覗いてみましょう。例えばサインインとサインアウトを司るSessionsコントローラー(app/controllers/sessions_controller.rb)は、以下のようになっています。
class SessionsController < ApplicationController allow_unauthenticated_access only: %i[ new create ] (1) rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_url, alert: "Try again later." } (2) def new (3) end def create (4) if user = User.authenticate_by(params.permit(:email_address, :password)) start_new_session_for user redirect_to after_authentication_url else redirect_to new_session_path, alert: "Try another email address or password." end end def destroy (5) terminate_session redirect_to new_session_path end end
(1)に記述されているallow_unauthenticated_accessメソッドは、前述のように認証が不要なアクションの指定です。ここでは、newアクションとcreateアクションが指定されています。これらのアクションは認証が必要なときに呼び出されるので、当たり前と言えます。
(2)に記述されているrate_limitは、Rails 8.0で実装されたレート制限のメソッドです。パラメータで制限内容と制限時のレスポンスを指定します。
- to、within:時間内のリクエスト数を制限(この場合は3分で10回)
- only:制限対象のアクション(この場合はcreate)
- with:レスポンス内容(この場合は"Try again later."メッセージとともにサインイン画面へリダイレクト)
制限されると既定では「429 Too Many Requests」がレスポンスされますが、withパラメータで内容をカスタマイズできます。
(3)はnewアクションで、サインイン画面を表示するだけです。(4)はcreateアクションすなわちサインインの実行で、指定されたメールアドレスとパスワードで認証(authenticate_by、has_secure_passwordメソッドによって利用できる)を実行し、成功すればセッションを開始して元のページ(after_authentication_url)にリダイレクトします。失敗時には、"Try another email address or password."メッセージとともにサインイン画面にリダイレクトします。
(5)はdestroyアクションすなわちサインアウト処理です。セッションを破棄して、サインインページへリダイレクトします。
ジェネレータによって生成される認証機能には、サインアップをはじめ、メール認証、SNS認証などはサポートされないので、そういった機能が必要な場合には自分で追加実装するか、あるいは従来からあるDiviseなどを利用することになります。ただしシンプルな分見通しは良いので、独自のカスタマイズを施す余地は十分にあると思われます。