パスワードリマインダー(忘れ)機能で、メールのURLのtokenの期限切れや不正な(存在しない)場合に、パスワード再設定ページが表示され、いざ変更しようとすると、token不正と怒られるのが不親切なので、メールのURLを開いた時に不正を知らせるように変更しました。

先ずはSpec

spec/requests/users/passwords_spec.rb を作成

require 'rails_helper'

RSpec.describe 'Users::Passwords', type: :request do
  shared_context '期限内のtoken作成' do
    before do
      @token = Faker::Internet.password
      user = FactoryBot.build(:user)
      user.reset_password_token = Devise.token_generator.digest(self, :reset_password_token, @token)
      user.reset_password_sent_at = Time.now
      user.save!
    end
  end
  shared_context '期限切れのtoken作成' do
    before do
      @token = Faker::Internet.password
      user = FactoryBot.build(:user)
      user.reset_password_token = Devise.token_generator.digest(self, :reset_password_token, @token)
      user.reset_password_sent_at = '0000-01-01'
      user.save!
    end
  end

  # GET /users/password/edit パスワード再設定
  describe 'GET /users/password/edit' do
    context '期限内のtoken' do
      include_context '期限内のtoken作成'
      it 'renders a successful response' do
        get "#{edit_user_password_path}?reset_password_token=#{@token}"
        expect(response).to be_successful
      end
    end
    context '期限切れのtoken' do
      include_context '期限切れのtoken作成'
      it 'パスワード再設定メール送信にリダイレクト' do
        get "#{edit_user_password_path}?reset_password_token=#{@token}"
        expect(response).to redirect_to(new_user_password_path)
      end
    end
    context '存在しないtoken' do
      it 'パスワード再設定メール送信にリダイレクト' do
        get "#{edit_user_password_path}?reset_password_token=not"
        expect(response).to redirect_to(new_user_password_path)
      end
    end
  end

  # PUT /users/password パスワード再設定(処理)
  describe 'PUT /users/password' do
    context '期限内のtoken' do
      include_context '期限内のtoken作成'
      it 'renders a successful response' do
        put user_password_path, params: { user: { reset_password_token: @token } }
        expect(response).to be_successful
      end
    end
    context '期限切れのtoken' do
      include_context '期限切れのtoken作成'
      it 'パスワード再設定メール送信にリダイレクト' do
        put user_password_path, params: { user: { reset_password_token: @token } }
        expect(response).to redirect_to(new_user_password_path)
      end
    end
    context '存在しないtoken' do
      it 'パスワード再設定メール送信にリダイレクト' do
        put user_password_path, params: { user: { reset_password_token: 'not' } }
        expect(response).to redirect_to(new_user_password_path)
      end
    end
  end
end

確認

$ rspec spec/requests/users/passwords_spec.rb
6 examples, 4 failure

Spec通るように実装

app/controllers/users/passwords_controller.rb を変更

  # GET /users/password/edit パスワード再設定
  def edit
    return redirect_to new_user_password_path, alert: invalid_token_message unless valid_token?(params[:reset_password_token])

    super
  end

  # PUT /users/password パスワード再設定(処理)
  def update
    return redirect_to new_user_password_path, alert: invalid_token_message unless valid_token?(resource_params[:reset_password_token])

    super
  end

  private

  # 有効なtokenかを返却
  # @return true: 有効期限内, false: 存在しないか、期限切れ
  def valid_token?(token)
    reset_password_token = Devise.token_generator.digest(self, :reset_password_token, token)
    resource = resource_class.find_by(reset_password_token: reset_password_token)
    resource.present? && resource.reset_password_period_valid?
  end

  # tokenエラーメッセージを返却
  def invalid_token_message
    t('activerecord.errors.models.user.attributes.reset_password_token.invalid')
  end

確認

$ rspec spec/requests/users/passwords_spec.rb
6 examples, 4 failure
$ rails s
-> http://localhost:3000/users/password/edit?reset_password_token=not

【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/370657dd0ab22e0842412252b5dc75199fb12fec

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です