deviseで認証メールのリンククリック時に認証しログイン画面に飛ばす


deviseで認証処理を実装時、confirmableのデフォルトだと、認証メールクリック時にActionController::UnknownFormatエラーが出た。

confirmations/showのviewが無い。
認証成功時にログイン画面に飛ばしつつ、認証失敗時にconfirmations/showをエラー画面として表示する。

versionは以下。

ruby: 2.6.5
rails: 6.0.1
devise: 4.7.1

confirmations_controllerのshowメソッドをoverride

controllers/users/confirmations_controller.rb
class Users::ConfirmationsController < Devise::ConfirmationsController
  ...

  # GET /resource/confirmation?confirmation_token=abcdef
  def show
    self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])

    if resource.nil? || resource.confirmed?
      # トークンが不正な場合、アカウント登録(パスワード登録)が済んでいる場合
      self.resource = resource_class.confirm_by_token(params[:confirmation_token])
      render :show
    elsif resource.is_confirmation_period_expired?
      # アカウント登録メールの期限が切れた場合
      resource.errors.add(:email, :confirmation_period_expired,
        period: Devise::TimeInflector.time_ago_in_words(resource_class.confirm_within.ago))
      render :show
    else
      # activate
      self.resource = resource_class.confirm_by_token(params[:confirmation_token])
      redirect_to new_user_session_path, notice: 'メールアドレスを確認しました。'
    end
  end
  ...
end

メソッド追加

models/users.rb
  def is_confirmation_period_expired?
    # メールアドレス確認メール有効期限チェック(期限はconfig/initializers/devise.rbのconfirm_withinで設定)
    self.confirmation_period_expired?
  end

controllers/show のviewを作成

views/users/confirmations/show.html.erb
<h2>エラー画面</h2>
<% if resource.errors.any? %>
  <article class="message is-danger">
    <div class="message-header">
      <p><%= pluralize(resource.errors.count, "error") %> prohibited this service from being saved:</p>
      <button class="delete" aria-label="delete"></button>
    </div>
    <div class="message-body">
      <ul>
      <% resource.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  </article>
<% end %>

routesも忘れずに

config/routes.rb
...
  devise_for :users,
    controllers: {
      ...
      confirmations: 'users/confirmations',
      ...
    },
...
  devise_scope :user do
    put 'confirmation', to: 'users/confirmations#show', as: :back_confirmation
...