15

Using Rails 3.2.

I have half a dozen controllers, and want to protect some (but not all) of them with http_basic_authenticate_with.

I don't want to manually add http_basic_authenticate_with to each controller (I could add another controller in the future and forget to protect it!). It seems the answer is to put it in application_controller.rb with an :except arg which would list the controllers that should not be protected. The problem is, the :except clause wants method names rather than external controller module names, e.g.:

http_basic_authenticate_with :name => 'xxx', :password => 'yyy', :except => :foo, :bar

So then I thought "Wait, since I already have the protected controllers grouped in routes.rb, let's put it there." So I tried this in my routes:

  scope "/billing" do
    http_basic_authenticate_with :name ...
    resources :foo, :bar ...
  end

But now I get

undefined method `http_basic_authenticate_with'

What's the best way to approach this?

shacker
  • 14,712
  • 8
  • 89
  • 89
  • 2
    One option is to have a `class ProtectedController < ApplicationController`, put `http_basic_authenticate` there and inherit protected controllers from it. – Sergio Tulentsev Feb 09 '13 at 19:48
  • If you happen to be using devise for authentication, you can enable basic auth with devise instead of rails - https://github.com/plataformatec/devise/wiki/How-To:-Use-HTTP-Basic-Authentication - might not be relevant in your case? – house9 Feb 10 '13 at 06:48
  • Sergio, that would put me back in the same place I'm trying to avoid - I'd have to manually add each new controller I want protected. If I was willing to do that, I could have simply put the http_base_authenticate line into each controller. – shacker Feb 10 '13 at 09:31
  • @house9 I had not heard of Devise, thanks for the tip. I'll check it out. – shacker Feb 11 '13 at 07:52

1 Answers1

49

Do it the way Rails does it.

# rails/actionpack/lib/action_controller/metal/http_authentication.rb

def http_basic_authenticate_with(options = {})
  before_action(options.except(:name, :password, :realm)) do
    authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
      name == options[:name] && password == options[:password]
    end
  end
end

All that http_basic_authenticate_with does is add a before_action. You can just as easily do the same yourself:

# application_controller.rb

before_action :http_basic_authenticate

def http_basic_authenticate
  authenticate_or_request_with_http_basic do |name, password|
    name == 'xxx' && password == 'yyy'
  end
end

which means you can use skip_before_action in controllers where this behavior isn't desired:

# unprotected_controller.rb

skip_before_action :http_basic_authenticate
Sam Blake
  • 657
  • 6
  • 13