0
0

More than 3 years have passed since last update.

【Rails】アカウントをアクティベート

Last updated at Posted at 2020-01-10
  • あらかじめDBには有効化されていない状態のレコードがある
  • 顧客ID、メールアドレス、電話番号を入力すると有効化メールが送信される
  • リンクを踏んで有効化、ユーザー情報更新、ログイン

という流れを実装したのでまとめます。

前提

  • customersテーブルにはactivation_digestactivated_atの2つのカラムが必要(無い場合は追加)

Model

  • attr_accessoractivation_token属性を有効化
  • トークンとダイジェストを生成、代入するメソッドを作成
  • 引数にトークンを取って、ダイジェストと比較するメソッドを作成
app/models/customer.rb
class Customer < ApplicationRecord
  attr_accessor :activation_token
.
.
  # 引数のハッシュ値を返す
  def self.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
    BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # ランダムトークン生成
  def self.new_token
    SecureRandom.urlsafe_base64
  end

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

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

Mailer

インスタンス変数を引数にとり、メールアドレスやトークンを抽出して送信します。
なお、本番環境のメール送信部分は未完成です。サーバーのログからメールの文面を確認してます。
【Rails】メールを送信する(ローカル)

app/mailers/customer_mailer.rb
class CustomerMailer < ApplicationMailer
  def account_activation(customer)
    @customer = customer
    mail to: customer.email, subject: 'アカウント有効化'
  end
end
app/views/customer_mailer/account_activation.text.erb
以下のアドレスにアクセスし、アカウントを有効化してください。
<%= edit_account_activation_url(id: @customer.activation_token, email: @customer.email) %>

Controller

  • newアクション:メールアドレス等からユーザー(アクティベートされてない)の検索フォームを表示する
  • createアクション:該当するユーザーが見つかったら、トークンとダイジェストを生成し、メールを送信
  • sentアクション:「メールを送信しました」という画面を表示する
  • editアクション:トークンとダイジェストを比較し、一致したらユーザー登録フォームを表示
  • updateアクション:ユーザー情報更新 + アカウント有効化
app/controllers/account_activations_controller.rb
class AccountActivationsController < PublicController
  #メールアドレス等からユーザー(アクティベートされてない)の検索フォームを表示する
  def new
    @customer = Customer.new
  end

  #該当するユーザーが見つかったら、トークンとダイジェストを生成し、メールを送信
  def create
    customer_id = params[:customer][:customer_id]
    email = params[:customer][:email]
    phone = params[:customer][:phone]
    @customer = Customer.find_by('(customer_id = ?) AND (email = ?) AND (phone = ?)', customer_id, email, phone)
    unless @customer
      flash.now[:danger] = '該当するユーザーが見つかりませんでした'
      @customer = Customer.new
      render :new and return
    end
    @customer.create_activation_digest
    @customer.save
    CustomerMailer.account_activation(@customer).deliver_now
    redirect_to account_activations_sent_url(customer_id: @customer.id)
  end

  #「メールを送信しました」という画面を表示する
  def sent
    @customer = Customer.find_by(customer_id: params[:customer_id])
  end

  #トークンとダイジェストを比較し、一致したらユーザー登録フォームを表示
  def edit
    @customer = Customer.find_by(email: params[:email])
    unless @customer
      flash[:danger] = 'ユーザーが見つかりませんでした'
      redirect_to login_url and return
    end
    unless @customer.activated_at.nil?
      flash[:danger] = 'このアカウントはすでに有効化されています'
      redirect_to login_url and return
    end
    unless @customer&.authenticated?(params[:id])
      flash[:danger] = 'エラーが発生しました。もう一度やり直してください。'
      redirect_to login_url and return
    end
  end

  #ユーザー情報更新 + アカウント有効化
  def update
    @customer = Customer.find_by(customer_id: params[:id])
    unless activation_params[:password].length >= 8
      flash.now[:danger] = 'パスワードは8文字以上で入力してください'
      render :edit and return
    end
    if @customer.update(activation_params)
      @customer.update_attributes(activated_at: Time.zone.now)
      log_in @customer
      render 'account_activations/complete'
    else
      flash.now[:danger] = '入力内容に誤りがあります'
      render :edit
    end
  end

  private

  def activation_params
    params.require(:customer).permit(:customer_id, :email, :nickname, :phone, :password, :password_confirmation)
  end
end

View

app/views/account_activations/new.html.erb
<%= form_with model: @customer, url: '/account_activations', local: true do |f| %>

 <%= f.text_field :customer_id %>
 <%= f.text_field :phone %>
 <%= f.text_field :email %>

 <%= f.submit value: '送信' %>
<% end %>
app/views/account_activations/sent.html.erb
<p>
ご入力いただいたメールアドレス(<%= @customer.email %>)に、確認用メールを送信しました。
メール本文に記載されているURLを開き、会員登録を完了させてください。
</p>
app/views/account_activations/edit.html.erb
<%= form_with model: @customer, url: "/account_activations/#{@customer.customer_id}", local: true do |f| %>
  <%= f.text_field :customer_id, readonly: true %>
  <%= f.text_field :phone, readonly: true %>
  <%= f.text_field :email, readonly: true %>
  <%= f.password_field :password, minlength: '8', required: 'true' %>
  <%= f.password_field :password_confirmation, minlength: '8', required: 'true' %>
  <%= f.text_field :nickname %>
  <%= f.submit '登録' %>
<% end %>
app/views/account_activations/complete.html.erb
<p class="box__toptxt">ご登録が完了いたしました。</p>
<%= link_to 'トップページ', root_path %>

ルーティング

config/routes.rb
resources :account_activations, only: %i[new create edit update]
get '/account_activations/sent', to: 'account_activations#sent'

問題点

Modelでパスワードに入れる値に制限をかけたら、editアクションで@customerを取得することができなくなってしまったので、仕方なくコントローラーでバリデーションの処理を実装しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0