Search code examples
ruby-on-railswardengrape-api

Configuring Warden with Grape API application


I have configured Warden in my Rails/Grape API app in config/initializers/warden.rb:

Rails.application.config.middleware.use Warden::Manager do |manager|
  manager.default_strategies :password
end

(I'd share the password strategy code, but it really doesn't apply to this question. I have it stored at config/initializers/warden/strategies/password.rb.)

When I run an RSpec rquest spec with invalid login credentials:

describe 'session_tokens', type: :request do
  let!(:user)      { FactoryGirl.create(:user) }
  let(:ip_address) { Faker::Internet.ip_v4_address }

  describe 'POST /' do
    context 'with invalid password' do
      before do
        post '/api/v1/session_tokens', email: user.email, password: "#{user.password}.", ip: ip_address
      end

      it 'is unsuccessful' do
        expect(response.code).to eql '401'
      end

      it 'has an "X-Error-Detail" header' do
        expect(response.header['X-Error-Detail']).to eql 'Invalid email or password.'
      end
    end
  end
end

It gives me this error:

Failure/Error: post '/api/v1/session_tokens', email: user.email, password: "#{user.password}.", ip: ip_address
RuntimeError:
   No Failure App provided

For the life of me, I cannot get Warden to work correctly with Grape after reviewing examples online (for example, dblock/grape_warden). Most examples are simple and set the Grape app itself as the failure app.

When I do pass a module within my Grape app as the failure_app:

Rails.application.config.middleware.use Warden::Manager do |manager|
  manager.default_strategies :password
  manager.failure_app = -> (env) { API::V1::SessionTokens }
end

I get this error, even though I have a post :unauthenticated block in that module:

Failure/Error: post '/api/v1/session_tokens', email: user.email, password: "#{user.password}.", ip: ip_address
NoMethodError:
  undefined method `unauthenticated' for API::V1::SessionTokens:Class

The same happens when I move the unauthenticated definition to the root of my Grape app.


Solution

  • The solution was to configure Warden within the Grape app itself instead of within a Rails initializer.

    I removed config/initializers/warden.rb and put its contents within my Grape app as such:

    module API
      module V1
        class Base < Grape::API
          mount API::V1::SessionTokens
    
          use Warden::Manager do |manager|
            manager.default_strategies :password
            manager.failure_app = API::V1::SessionTokens
          end
        end
      end
    end
    

    Works perfectly now!

    Kudos to Jean Bahnik for this file on GitHub in particular. I found this beautiful piece of code after almost giving up.