【Rails7】ユーザー新規登録時にアカウントを有効化する(メール認証)

Railsチュートリアル第11章にて、メール認証によるアカウントの有効化について学んだので、その復習とメモ用に本記事を残そうと思います。

ここでは、すでにログイン機構(チュートリアル第8章)、およびRemember me機能(チュートリアル第9章)を実装済みの上で話を進めて行きます。

特にメソッドに関してはログイン機構で作成したものをそのまま使い回す、もしくは変更して使う場合が多いので、1から流れを理解したい方はログイン機構の作成から順に進めていくことをお勧めします。

あわせて読みたい
【Rails7】ユーザー認証機能(ログイン・ログアウト)を実装するまでの一連の流れ Railsでサービスを開発しようとなると避けて通れない(と思う)のが、ユーザーの認証機能。 認証機能を1から作るのはかなり大変ですが、幸いRailsにはDeviceという簡単...
あわせて読みたい
【Rails7】Cookieを使ってログイン状態を保持する(Remember me 機能の実装) Railsチュートリアル第8章にて、基本的なログイン・ログアウト機能の作成はできましたが、現状だとブラウザを閉じるとセッション情報が失われてしまいます。 (ブラウザ...
目次

開発環境

  • Ruby 3.1.2
  • Ruby on Rails 7.0.3
  • M1 Macbook Air 2020
  • mac OS Monterey (ver. 12.4)
  • ターミナル bash (Rosetta 2 使用)

ソースコード

GitHub
GitHub - hirokirokki0820/user-authentication Contribute to hirokirokki0820/user-authentication development by creating an account on GitHub.

ユーザー認証機能(アカウント有効化)の仕上がりイメージ

今回実装するユーザー認証の、システム上の動作イメージは以下の通りです。

  1. ユーザーの初期状態は「有効化されていない」(unactivated)にしておく
  2. ユーザーが新規アカウント登録する(ユーザー名、メールアドレス、パスワードなどを入力し、送信する)
  3. 有効化トークン(activation_token)を生成する
  4. ③に対応する有効化ダイジェスト(activation_digest)を生成し、DBに保存する
  5. 有効化トークン(activation_token)とメールアドレスを含めた有効化リンクをメールに添付し、ユーザーに送信する
  6. ユーザーがメールを開き、有効化リンクをクリックする
  7. ⑥に該当するユーザーをDBから(メールアドレスをキーとして)検索し、保存された有効化ダイジェストと比較することで有効化トークンを認証する
  8. ユーザーの状態を「有効化済み」(activated)に変更する

こうすることで、メールアドレスによるユーザー認証を行うことができます。

実際にWebアプリケーション登録時に送信されるメールは以下のようなイメージです。

メールに記載のアカウント有効化リンクをクリックすることで、ユーザーの状態はactivatedとなり、正式に登録されたとみなされます。

ユーザー認証機能(アカウント有効化)を実装する具体的な手順

まず最初に、ユーザー認証機能を実装する大まかな流れを以下に示します。

  1. AccountActivationsコントローラを生成する
  2. ハッシュ化したトークンを保存する有効化ダイジェストactivation_digest、および有効化の状態activated、有効化の日時activated_atを示すカラムを生成する
  3. Userモデルにアカウント有効化のコードを追記する
  4. アカウント有効化メールのテンプレート(Userメイラー)作成
  5. 送信メールのプレビュー
  6. ユーザー登録時にアカウント有効化メールを送信する
  7. 他の認証でも使えるようにauthenticated?メソッドを一般化する
  8. アカウントを有効化する(editアクションで有効化)

こんな感じです。

今回は基本的なユーザー認証機能(ログイン・ログアウト、Remember me)はすでにできており、それに機能追加する形で進めていきます。

それでは、それぞれの項目について詳しく見ていきましょう。

①AccountActivationsコントローラを生成する

UsersリソースやSessionsリソースのときと同様に、AccountActivationsリソースを作るためにはまずはAccountActivationsコントローラを生成します。

$ rails g controller AccountActivations

また、config/routes.rbを以下のように設定します。

Rails.application.routes.draw do
  root "home#index"
  get "about", to: "home#about"
  get "signup", to: "users#new"
  get "login", to: "sessions#new"
  post "login", to: "sessions#create"
  delete "logout", to: "sessions#destroy"
  resources :users, except: [:new]
  resources :account_activations, only: [:edit]
end

②ハッシュ化したトークンを保存する有効化ダイジェスト(activation_digest)、および有効化の状態(activated)、有効化の日時(activated_at)を示すカラムを生成する

続いては、アカウントの有効化に必要なカラムを追加していきます。

今回必要なカラムは以下の3つです。

  1. 有効化ダイジェスト…「activation_digest
  2. 有効化の状態…「activated
  3. 有効化の日時…「activated_at
追加するカラム名
activation_digeststring
activatedboolean
activated_atdatetime

これらを、以下のコマンドを実行して追加します。

$ rails g migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime

マイグレーションファイルが生成されるので、中身を開いて確認します。

class AddActivationToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :activation_digest, :string
    add_column :users, :activated, :boolean
    add_column :users, :activated_at, :datetime
  end
end

追加するカラム名などに間違いがなければ、マイグレーションを実行してカラムを追加します。

$ rails db:migrate

③Userモデルにアカウント有効化のコードを追記する

まずは、トークンを一時保存しておくための仮装属性をUserモデル内に定義しておきます。

attr_accessor :activation_token

これで、activation_token属性へのアクセス(読み書き)ができるようになりました。

次に、生成したトークンをactivation_token属性に代入すると同時に、ハッシュかしてDB内に保存するためのメソッドをprivateメソッドとして定義します。

  ・・・

  private
  # 有効化トークンとダイジェストを作成及び代入する
  def create_activation_digest
    self.activation_token = User.new_token
    self.activation_digest = User.digest(activation_token)
  end

end

上記のメソッドをオブジェクトの作成(create)時に呼び出すようにするため、before_createコールバックを使って以下のように記述します。

(新規ユーザー登録時にこのメソッドが呼び出される)

before_create :create_activation_digest

最終的に、user.rbに記述する内容は以下のようになります。

class User < ApplicationRecord
  attr_accessor :remember_token, :activation_token
  before_save   :downcase_email
  before_create :create_activation_digest

  ・・・

  private
    # メールアドレスを全て小文字にする
    def downcase_email
      self.email = email.downcase
    end

    # 有効化トークンとダイジェストを作成及び代入する
    def create_activation_digest
      self.activation_token = User.new_token
      self.activation_digest = User.digest(activation_token)
    end
end

④アカウント有効化メールのテンプレート(Userメイラー)作成

モデルやコンローラーと同様に、メイラーも以下のコマンドで生成できます。

$ rails g mailer UserMailer account_activation

続いて、作成したUserメイラーuser_mailer.rbを以下のように編集します。

class UserMailer < ApplicationMailer

  def account_activation(user)
    @user = user
    mail to: user.email, subject: "Account activation"
  end

end

ここでは送信メール本文にユーザー名を含め、カスタムの有効化リンクを追加します。

この後、DBからユーザーをメールアドレスで検索して有効化トークンを認証できるようにするために、リンクには有効化トークンとメールアドレスを両方含めておく必要があります。

ここで、以下のようなURLパスを指定するメソッドについて思い出してみましょう。

edit_user_url(user)

これは、usersコントローラーのeditアクションへのパス(userを引数)を表していますね。

上記のメソッドは、次の形式のURLを生成します。

http://localhost:3000/users/1/edit

これと同様に、account_activationsコントローラーのeditアクションへのパス(@userの有効化トークンを引数)を指定する場合は、

edit_account_activation_url(@user.activation_token)

これに対応するアカウント有効化リンクのベースURLは以下のようになります。

http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit

上記URLには有効化トークンX9FkWNOAQru914FbR-rdAQ(Users.new_tokenメソッドで自動生成)が含まれていることがわかります。

(この有効化トークン、先ほどの...users/1/editid=1に相当すると考えれば分かりやすいと思います)

今回はベースURLにメールアドレスも含ませたいので、有効化リンクのパス(有効化トークンとメールアドレスを両方含んだURL)は以下のように表されます。

edit_account_activation_url(@user.activation_token, email: @user.email)

メールアドレスはクリエパラメータ(URLの末尾で疑問符「?」に続けてキーと値のペアを記述したもの)を使うことで有効化リンク(ベースURL)に組み込むことができます。

最終的に、有効化リンクのURLは以下のように表されます。

(メールアドレスはsample@example.comとしています)

http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com

ここで、%40となっているのは、@などの特殊記号はURLでは扱えないため、自動的にエスケープされたためです。

(後でparams[:email]でメールアドレスを読み込むときにエスケープは自動的に解除されます。)

それでは、上記の内容を踏まえてメールのテンプレートを作成していきましょう。

Userメイラー追加時にapp/views/user_mailerディレクトリ内に自動生成されたテンプレート(html用、text用)を以下のように編集します(文章は各自好きな文言を入れてください)。

<h1>User Authentication App</h1>
<p>こんにちは、<%= @user.name %>さん。</p>
<p>
この度は◯◯Webサービスに仮登録いただきありがとうございます。
</p>
<p>
つきましては、下記リンクをクリックしてアカウントを有効化してください。
</p>

<%= link_to "アカウントを有効化する", edit_account_activation_url(@user.activation_token, email: @user.email) %>
こんにちは、<%= @user.name %>さん。

この度は◯◯Webサービスに仮登録いただきありがとうございます。
つきましては、下記リンクをクリックしてアカウントを有効化してください。

<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>

テンプレートはこんな感じでOKです。

⑤送信メールのプレビュー

送信するメールがどのような感じになるのか、あらかじめ確認しておきたい場合に便利なのがメールのプレビュー機能です。

Railsのメール送信のプレビューでは、HTMLメール、Textメールの両方のバージョンのプレビューを確認することができます。

ここでは、このプレビュー機能を利用するための設定をしていきます。

まず、config/environments/development.rbを以下のように設定します。

Rails.application.configure do

 ・・・

  host = 'localhost:3000'
  config.action_mailer.default_url_options = { host: host, protocol: 'http' }

 ・・・

end

上記の環境設定は、お手持ちのPC(OS)の環境によって異なる場合があるので注意。大体は上記の設定でOKなはずです。

続いては、Userメイラー生成時に自動生成されたプレビューファイルの設定をしていきます。

test/mailers/previewsディレクトリに自動生成されたuser_mailer_preview.rbを以下のように編集します。

class UserMailerPreview < ActionMailer::Preview

  # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end

end

ここで、コード中にコメントアウトされた状態で記載されている以下のようなURL

http://localhost:3000/rails/mailers/user_mailer/account_activation

をブラウザのアドレスバーに入力すると、送信メールのプレビューを表示することができます。(railsサーバーが起動した状態で)

⑥新規ユーザー登録時にアカウント有効化メールを送信する

usersコントローラーのcreateアクションに以下のように記述します。

class UsersController < ApplicationController

  ・・・

  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.account_activation(@user).deliver_now
      flash[:notice] = "アカウント有効化メールを送信しました。メールが届きましたら、記載されているリンクをクリックしてアカウントを有効化してください。"
      redirect_to root_url
    else
      render :new, status: :unprocessable_entity
    end
  end

 ・・・

end

ここで、メールを送信する処理を行う以下のコード、

UserMailer.account_activation(@user).deliver_now

これが何をしているのかというと、

Userメイラーのaccount_activation(user)メソッドを実行してテンプレートを呼び出し、引数として指定した@user宛てにdeliver_nowで今すぐメールを送信する

といった処理をしています(実際にメールを送信するためには、別途メールサーバーの設定が必要です)。

class UserMailer < ApplicationMailer

  def account_activation(user)
    @user = user
    mail to: user.email, subject: "Account activation"
  end

end

ここで、メールを送信する一連の処理をメソッド化してuser.rb内に記述しておきます。

class User < ApplicationRecord

 ・・・

  # 有効化用のメールを送信する
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end

  private

 ・・・

end

こうすることで、メール送信時のコードを以下のようにシンプルにすることができます。

@user.send_activation_email

usersコントローラーのcreateアクションに記述する、最終的なコードは以下の通りです。

class UsersController < ApplicationController

  ・・・

  def create
    @user = User.new(user_params)
    if @user.save
      # UserMailer.account_activation(@user).deliver_now を下記のように変更
      @user.send_activation_email
      flash[:notice] = "アカウント有効化メールを送信しました。メールが届きましたら、記載されているリンクをクリックしてアカウントを有効化してください。"
      redirect_to root_url
    else
      render :new, status: :unprocessable_entity
    end
  end

 ・・・

end

⑦他の認証でも使えるようにauthenticated?メソッドを一般化する

アプリケーションから有効化リンクをメール送信できる準備が整ったら、次はメールに記載された有効化リンクをクリックした時の認証を行うメソッドを準備します。

有効化リンクをクリックした時の認証は、次のようなコードで行うことにします。

user = User.find_by(email: params[:email])
if user && user.authenticated?(:activation, params[:id])

認証の大まかな流れとしては、

  1. 有効化リンクに埋め込まれたメールアドレスからDB内のユーザーを検索
  2. 該当するユーザーの有効化ダイジェスト(ハッシュ化されたトークン)と、有効化リンクに埋め込まれたトークンが一致するか比較
  3. 一致したらtrueを返し、アカウントを有効化する処理へ進む

といった感じです。

このトークンの認証はチュートリアル第9章の「Remember me」機能の実装で以下のように定義してありました。

# トークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
  return false if remember_digest.nil?
  BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

上記のメソッドとは別に、アカウント有効化用のauthenticated?メソッドを別途定義しても良いのですが、RailsのDRYにのっとり極力コード量を減らしたいので使いまわせる形にするのが理想です。

(remember_token, remember_digest の、rememberの部分をactivationという文字列に置き換えたい)

そこで、今回はsendメソッドという魔法のようなメソッドを使い、他の認証時にも使いまわせるようauthenticated?メソッドを一般化したいと思います。

sendメソッドを用いることで、受け取ったパラメータに応じて呼び出すメソッドを切り替えることができるようになります。

以下、DB内の最初のユーザーが持つactivation_digest属性にアクセスする例を見てみましょう。

irb(main):001:0> user = User.first                    
irb(main):003:0> user.activation_digest
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):004:0> user.send(:activation_digest)
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):005:0> user.send("activation_digest")
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):006:0> attribute = :activation
=> :activation
irb(main):008:0> user.send("#{attribute}_digest")
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"

ここで、

> user.activation_digest
=> “$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra”

> user.send(“#{attribute}_digest”)
=> “$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra”

この両者が等価である点に注目です。

後者の例では、シンボル:activationと等しいattribute変数を定義し、文字列の式展開(interpolation)を使って引数を正しく組み立ててから、sendに渡しています。

つまり、sendメソッドを使うことで、同じauthenticated?メソッドでも、

attribute変数の部分を(:remember や :activation などに)入れ替えるだけで、認証に応じたメソッドを自由に切り替えることができるのです。

このsendメソッドの性質を利用してremember me 機能実装時に定義したauthenticated?メソッドを書き換えてみましょう。

def authenticated?(remember_token)
  digest = self.send("remember_digest")
  return false if digest.nil?
  BCrypt::Password.new(digest).is_password?(remember_token)
end

上のコードの各引数を一般化し、文字列の式展開も利用すると、次のようなコードに置き換えることができます。

# 一般化されたauthenticated?メソッド
# Userモデル内では「self.」は省略可
def authenticated?(attribute, token)
  digest = self.send("#{attribute}_digest")
  return false if digest.nil?
  BCrypt::Password.new(digest).is_password?(token)
end

このコードをUserモデルuser.rbに記述します。

Userモデル内では「self.」の記述は省略できるので、最終的には以下のように記述できます。

class User < ApplicationRecord

 ・・・

  # 渡されたトークンがダイジェストと一致したらtrueを返す
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

 ・・・

end

ここで、sessions_helper.rbcurrent_userメソッドのauthenticated?が古いままで、引数が2つではなくまだ1つのままになっているので忘れないうちに変更しておきます。

  ・・・

  # 変更前のコード
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.encrypted[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end

  # ↓↓↓↓↓ 以下のようにコードを変更
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.encrypted[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(:remember, cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end

  ・・・

end

⑧アカウントを有効化する(editアクションで有効化)

ユーザー新規登録(仮登録)時にご自身のメールアドレス宛に以下のような有効化リンクが送られてきたとします。

http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com

この有効化リンクをクリックすると、どのようにアカウントが有効化されるのでしょうか。

上記の有効化リンク、ちょっと複雑そうに見えますが、基本構造は以下のようなパターンに似ています。

http://localhost:3000/users/1/edit

これは、usersコントローラーの(ユーザーidが1の)editアクションを実行するURLですね。

ここで、usersコントローラー内でparams[:id]を使うことでユーザーid(1)を取得することができます。

では、今回の有効化リンクに当てはめて見ましょう。

http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit

これは、account_activationsコントローラーの(トークンidがX9FkWNOAQru914FbR-rdAQの)editアクションを実行するURLですね。

こちらも同様に、account_activationsコントローラー内でparams[:id]を使うことで有効化トークン(X9FkWNOAQru914FbR-rdAQ)を取得することができます。

このURLにクリエパラメータを使ってメールアドレスを組み込んだものが以下のような有効化リンクになります。

http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com

(URLにメールアドレスを組み込む際に、特殊記号「@」は自動的にエスケープされます)

ちなみに、組み込んだメールアドレスも同様にparams[:email]を使うことで取り出すことができます(エスケープは読み込み時に自動的に解除される)。

以上の内容を踏まえて、この有効化リンクをクリックした時にアカウントを有効化するためには、

リンクへアクセス時にparams[:email]とparams[:id]から有効化トークンとメールアドレスを取得して認証するよう、account_activationsコントローラーのeditアクションに記述します。

具体的な流れは以下の通り。

  1. params[:email]でemail=sample%40example.comを取得(%40は自動的にエスケープ解除される)し、該当するユーザーをDBから検索
  2. params[:id]で有効化トークンX9FkWNOAQru914FbR-rdAQを取得
  3. 取得したトークンとDBにハッシュ化して保存されたトークン(該当するユーザーのもの)をauthenticated?メソッドで比較
  4. trueの場合はアカウントを有効化する(falseの場合は有効化せずにroot_urlにリダイレクト)

これらの一連の処理の流れを、account_activationsコントローラーに以下のように記述します。

class AccountActivationsController < ApplicationController
  def edit
    user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      user.activate
      log_in user
      flash[:notice] = "アカウントが有効化されました"
      redirect_to user
    else
      flash[:error] = "この有効化リンクは無効です"
      redirect_to root_url
    end
  end
end

アカウントを有効化するactivateメソッドは別途user.rb内に定義します。

class User < ApplicationRecord

 ・・・

  # アカウントを有効にする
  def activate
    update_attribute(:activated, true)
    update_attribute(:activated_at, Time.zone.now)
  end

  private

 ・・・

end

ここで、

update_attribute(:activated, true)

とすることで、アカウントを有効化しています。(デフォルトではfalseに設定されている)

これで、アカウントの有効化ができるようになりました。

ただ、今のままだと有効化されていないユーザー(:activatedfalseのユーザー)までログインできてしまうので、

有効化されていないユーザーがログインできないようにするよう、sessionsコントローラーのcreateアクションにて、

if user.activated?

で条件分岐し、有効化済みのユーザーのみログインできるようにします。

class SessionsController < ApplicationController

 ・・・

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      if user.activated?
        log_in(user)
        params[:session][:remember_me] == "1" ? remember(user) : forget(user)
        flash[:notice] = "ログインに成功しました"
        redirect_to user
      else
        message = "アカウントが有効化されていません。"
        message += "届いたメールをご確認の上、アカウントを有効化してください。"
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:error] = "ログインに失敗しました"
      render "new", status: :unprocessable_entity
    end
  end

 ・・・

end

これで今回実装したかったユーザー認証(アカウント有効化)は完成です。

【追記】有効化(認証)リンクに有効期限を設定する

アカウント有効化の実装はできましたが、このままだとユーザーがアカウント認証を済ませるまでメールのリンクは(半永久的に)有効となります。

つまり、もし万が一アカウントを有効化する前に認証メールが第三者に渡ってしまった場合、その第三者が認証を済ませてアカウントを不正利用してしまうかもしれません。

そこで、この認証メールの有効化リンクに有効期限を持たせることで、上記のリスクを極力回避するようにします。

有効化リンクに有効期限を持たせるためには、その有効化リンクが送信された日時の情報が必要になるので、

まずはusersテーブルに有効化リンクの送信日時を示すカラムactivation_sent_atを追加します。

$ rails g migration add_activation_sent_at_to_users activation_sent_at:datetime

マイグレーションファイルが生成されるので、中身を開いて確認します。

class AddActivationSentAtToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :activation_sent_at, :datetime
  end
end

追加するカラム名などに間違いがなければ、マイグレーションを実行してカラムを追加します。

$ rails db:migrate

続いて、user.rbにメール送信時にactivation_sent_atに現在時刻を更新するメソッド、および有効化リンクの有効期限(今回は24時間に設定)を設定するメソッドをそれぞれ記述します。

 ・・・

  # 有効化用のメールを送信する
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
    #-------- 追記(メール送信時の日時を更新する) ---------
    update_attribute(:activation_sent_at, Time.zone.now)
    #------------------------------------------------
  end

  ・・・
  #---------------- 追記 --------------------
  # 有効化リンクの有効期限(24時間)が切れている場合はtrueを返す
  def activation_expired?
    activation_sent_at < 24.hours.ago
  end
  #------------------------------------------
  private
  ・・・

最後に、account_activations_controller.rbを以下のように編集します。

class AccountActivationsController < ApplicationController

  def edit
    @user = User.find_by(email: params[:email])
    # --------- !@user.activation_expired? を追記 ---------------
    if @user && !@user.activated? && !@user.activation_expired? && @user.authenticated?(:activation, params[:id])
    # ----------------------------------------------------------
      @user.activate
      log_in @user
      flash[:notice] = "アカウントが有効化されました"
      redirect_to @user
    else
      flash[:error] = "このリンクは無効、もしくは有効期限切れです"
      # ------- 追記 --------
      @user.destroy if @user
      # --------------------
      redirect_to signup_url
    end
  end

end

ここで、else以下の@user.destroy if @userとすることで、アカウント認証に失敗した場合はデータベース内から仮登録したユーザー情報を削除するようにしています。

もし、認証に失敗したのに仮登録したユーザーがデータベースに残ったままだと、再登録しようとしても同じメールアドレスで登録できなくなってしまうからです。

本番環境でのメール送信(SendGrid)

アカウントの有効化機能の実装ができたら、実際に本番環境でメールが送信できるか確認してみましょう。

本番環境(および開発環境)におけるメールサーバーの設定方法は下記記事を参考にしてみてください。

あわせて読みたい
【Rails7】開発環境、本番環境でメール送信できるようにする(SendGrid, Gmail) Railsで作ったアプリからメールを送信したい場合、外部のメールサーバーを利用してメールを送ることになります。 今回は、Railsのアプリ制作でよく取り上げられているメ...

新規ユーザー登録時に、以下のようなアカウント有効化メールが届くかと思います。

(Gメールアドレス宛てに届いたhtmlメールの例です。メールサーバーをSendGridにした場合、送信元に以下のように「sendgrid.net 経由」と記載されているかと思います)

アカウント有効化リンクをクリックして、問題なければOKです。

以上です。

お疲れ様でした。

【続き】パスワードの再設定を実装する流れ

あわせて読みたい
【Rails7】パスワードの再設定を実装する流れ Railsチュートリアル第11章でアカウントの有効化の実装が完了し、ユーザーの本人確信ができるようになったので、次はパスワードを忘れた時のパスワードの再設定機能を実...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次