👓
Devise Token Authで/sing_outを呼び出すとundefined method `destroy'が出た話
問題
$ bundle exec rspec
Failures:
1) Api::V1::Sessions logout as an authenticated user success logout and APIs require authentication result in a 401 error
Failure/Error: delete '/api/v1/auth/sign_out', headers: auth_params
NoMethodError:
undefined method `destroy' for {"warden.user.user.key"=>#<User email: "test2@example.com", provider: "email", uid: "test2@example.com", id: 100, allow_password_change: [FILTERED], name: "TestUser", nickname: nil, image: nil, created_at: "2022-10-17 13:21:07.448428000 +0000", updated_at: "2022-10-17 13:21:07.461190000 +0000"}:RackSessionFixController::FakeRackSession
session.destroy
^^^^^^^^
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:36:in `block in call'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:34:in `catch'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:34:in `call'
# /usr/local/bundle/gems/rack-2.2.4/lib/rack/etag.rb:27:in `call'
# /usr/local/bundle/gems/rack-2.2.4/lib/rack/conditional_get.rb:40:in `call'
# /usr/local/bundle/gems/rack-2.2.4/lib/rack/head.rb:12:in `call'
# /usr/local/bundle/gems/railties-7.0.4/lib/rails/rack/logger.rb:40:in `call_app'
# /usr/local/bundle/gems/railties-7.0.4/lib/rails/rack/logger.rb:25:in `block in call'
# /usr/local/bundle/gems/railties-7.0.4/lib/rails/rack/logger.rb:25:in `call'
# /usr/local/bundle/gems/rack-2.2.4/lib/rack/runtime.rb:22:in `call'
# /usr/local/bundle/gems/rack-2.2.4/lib/rack/sendfile.rb:110:in `call'
# /usr/local/bundle/gems/railties-7.0.4/lib/rails/engine.rb:530:in `call'
# /usr/local/bundle/gems/rack-test-2.0.2/lib/rack/test.rb:358:in `process_request'
# /usr/local/bundle/gems/rack-test-2.0.2/lib/rack/test.rb:155:in `request'
# ./spec/requests/api/v1/sessions_api_spec.rb:36:in `block (5 levels) in <top (required)>'
# ./spec/requests/api/v1/sessions_api_spec.rb:35:in `block (4 levels) in <top (required)>'
Finished in 0.09395 seconds (files took 0.64071 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/requests/api/v1/sessions_api_spec.rb:34 # Api::V1::Sessions logout as an authenticated user success logout and APIs require authentication result in a 401 error
環境
- Ruby 3.1.2
- Rails 7.0.4
- devise 4.8.1
- devise_token_auth 1.2.1
$ rails routes
Prefix Verb URI Pattern Controller#Action
new_api_v1_user_session GET /api/v1/auth/sign_in(.:format) api/v1/sessions#new
api_v1_user_session POST /api/v1/auth/sign_in(.:format) api/v1/sessions#create
destroy_api_v1_user_session DELETE /api/v1/auth/sign_out(.:format) api/v1/sessions#destroy
cancel_api_v1_user_registration GET /api/v1/auth/cancel(.:format) api/v1/registrations#cancel
new_api_v1_user_registration GET /api/v1/auth/sign_up(.:format) api/v1/registrations#new
edit_api_v1_user_registration GET /api/v1/auth/edit(.:format) api/v1/registrations#edit
api_v1_user_registration PATCH /api/v1/auth(.:format) api/v1/registrations#update
PUT /api/v1/auth(.:format) api/v1/registrations#update
DELETE /api/v1/auth(.:format) api/v1/registrations#destroy
POST /api/v1/auth(.:format) api/v1/registrations#create
api_v1_auth_validate_token GET /api/v1/auth/validate_token(.:format) devise_token_auth/token_validations#validate_token
コード
sessions_controller.rb
class Api::V1::SessionsController < DeviseTokenAuth::SessionsController
end
application_controller.rb
class ApplicationController < ActionController::API
include DeviseTokenAuth::Concerns::SetUserByToken
# Rails 7でDeviseを使用するめのパッチ
# https://github.com/heartcombo/devise/issues/5443
include RackSessionFixController
end
rack_session_fix_controller.rb
module RackSessionFixController
extend ActiveSupport::Concern
class FakeRackSession < Hash
def enabled?
false
end
end
included do
before_action :set_fake_rack_session_for_devise
private
def set_fake_rack_session_for_devise
request.env['rack.session'] ||= FakeRackSession.new
end
end
end
sessions_api_spec.rb
require 'rails_helper'
require 'json'
RSpec.describe 'Api::V1::Sessions', type: :request do
describe 'logout' do
context 'as an authenticated user' do
let(:user) { FactoryBot.attributes_for(:user) }
it 'success logout and APIs require authentication result in a 401 error' do
auth_params = sign_in(user)
aggregate_failures do
delete '/api/v1/auth/sign_out', headers: auth_params
expect(response).to have_http_status(:success)
get '/api/v1/mypage', headers: auth_params
expect(response).to have_http_status(401)
end
end
end
end
end
対応方法
sessions_controller.rb
class Api::V1::SessionsController < DeviseTokenAuth::SessionsController
skip_after_action :reset_session, only: [:destroy]
def destroy
super
session["warden.user.user.key"] = nil
end
end
または
sessions_controller.rb
class Api::V1::SessionsController < DeviseTokenAuth::SessionsController
private
def reset_session
session["warden.user.user.key"] = nil
end
end
reset_session
が走るとエラーとなる模様。そのためreset_session
をスキップ or 再定義することでエラーとならないように対応しました。
reset_session
はRailsのドキュメントを見る限り、セッション情報を削除したいだけみたいなので、自前でセッション削除処理を追加してみたら、エラーが解消されました。
Discussion