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.
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.