【Rails】Devise gemは未認証XHRに対してリダイレクトでなく401 Unauthorizedで応答できる

Devise gemconfig.http_authenticatable_on_xhrtrue のとき、未認証XHRに対してリダイレクトするのではなく、401 Unauthorizedで応答します。

config/initializers/devise.rb
Devise.setup do |config|
  config.http_authenticatable_on_xhr = true
end

specは下記のようになります。
※context「XHRのとき」とcontext「非XHRのとき」の振る舞いの差はDevise由来なので、どちらかだけでも十分かと思います。

`describe 'GET /api/v1/posts' do
  subject(:req) do
    post api_v1_posts_path, headers: headers
    response
  end

  let(:headers) { {} }

  context 'ログインしていないとき' do
    context 'XHRのとき' do
      let(:headers) { { 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' } }

      it '401 Unauthorizedで応答する' do
        is_expected.to have_http_status(:unauthorized)
      end
    end

    context '非XHRのとき' do
      it '管理者サインイン画面にリダイレクトする' do
        is_expected.to redirect_to('/admins/sign_in')
      end
    end
  end

  context '管理者としてログインしているとき' do
    let(:admin) { create(:admin) }

    before { sign_in admin }

    it '200 OKで応答する' do
      is_expected.to have_http_status(:ok)
    end
  end
end

仕組み

WardenがXHRリクエストであるかを判定し、DeviseのFailureAppが具体的な応答を決めています。

(仕組みについては、GitMCPを利用して色々と調べると勉強になりました。MCPは楽しいですね)

経緯

既存プロジェクトの改修範囲をカバーするためのrequest specについて、初めはこのように書いていました:

describe 'GET /api/v1/posts' do
subject(:req) do
post api_v1_posts_path
response
end

context 'ログインしていないとき' do
it '管理者サインイン画面にリダイレクトする' do
is_expected.to redirect_to('/admins/sign_in')
end
end

context '管理者としてログインしているとき' do
let(:admin) { create(:admin) }

before { sign_in admin }

it '200で応答する' do
is_expected.to have_http_status(:ok)
end
end
end

これでもNGにはならないのですけど、「APIなのにリダイレクト?」と疑問に思ってE2Eで確認したところ401で応答していたので、本稿の調査となりました。

環境

rails (7.1.5.1)
devise (4.9.4)
rspec-rails (7.1.1)
役に立ったらシェアしていただけると嬉しいです
  • URLをコピーしました!
  • URLをコピーしました!
目次