Search code examples
rubysinatrarackwarden

Unable to use Warden in Sinatra App: env['warden'] returns nil


I'm writing a Sinatra Rack App and I want to use Warden for authentication. I'm using heroku's toolbelt so I use foreman to run my app. I've found some code that's presumably supposed to get this working. Unfortunately, when I attempt to actually access the Warden env object, it is nil.

I've attempted to use the sinatra_warden gem, but it also has its own bugs (might be related to this one).

config.ru:

require './web.rb'
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
run MyApp

web.rb:

require 'sinatra'
require 'warden'
require 'data_mapper'

require './config/datamapper.rb'
require './config/warden.rb' # I've tried this inside of MyApp, still didn't work

class MyApp < Sinatra::Base

  get '/test' do
    env['warden'].authenticate! # env['warden'] is nil :(
  end

end

config/warden.rb:

use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']

use Warden::Manager do |manager|
  manager.default_strategies :password
  manager.failure_app = MyApp.new
end

Warden::Manager.serialize_into_session { |user| user.id }
Warden::Manager.serialize_from_session { |id| User.get(id) }

Warden::Manager.before_failure do |env,opts|
  # Sinatra is very sensitive to the request method
  # since authentication could fail on any type of method, we need
  # to set it for the failure app so it is routed to the correct block
  env['REQUEST_METHOD'] = "POST"
end

Warden::Strategies.add(:password) do
  def valid?
    params["email"] || params["password"]
  end

  def authenticate!
    u = User.authenticate(params["email"], params["password"])
    u.nil? ? fail!("Could not log in") : success!(u)
  end
end

Versions:

  • Sinatra: 1.1.0
  • Warden: 1.2.1
  • Rack: 1.4.1
  • Ruby: 1.9.3p194
  • Foreman: 0.60.0

Any ideas how to use Warden the set up I've described?

(P.S. Out of curiosity, what exactly is the env variable?)


Solution

  • Rack internally uses the class Rack::Builder to parse your config.ru file and wrap directives to build up the middleware components.

    I believe your builder calls to use in config/warden.rb are getting ignored. It may work to remove the directives from that file and add them to the middleware stack in config.ru:

    require './web.rb'
    
    use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']
    
    use Warden::Manager do |manager|
      manager.default_strategies :password
      manager.failure_app = MyApp.new
    end
    
    use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
    
    run MyApp