パスワードリマインダー(忘れ)機能で、メールの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
